 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.ui.internal;

 import java.io.BufferedInputStream ;
 import java.io.BufferedReader ;
 import java.io.File ;
 import java.io.FileInputStream ;
 import java.io.FileOutputStream ;
 import java.io.IOException ;
 import java.io.InputStream ;
 import java.io.InputStreamReader ;
 import java.io.OutputStreamWriter ;
 import java.lang.reflect.Method ;
 import java.util.ArrayList ;
 import java.util.Arrays ;
 import java.util.Dictionary ;
 import java.util.HashSet ;
 import java.util.Hashtable ;
 import java.util.List ;
 import java.util.Set ;

 import org.eclipse.core.commands.Command;
 import org.eclipse.core.commands.CommandManager;
 import org.eclipse.core.commands.common.EventManager;
 import org.eclipse.core.commands.contexts.ContextManager;
 import org.eclipse.core.databinding.observable.Realm;
 import org.eclipse.core.runtime.Assert;
 import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.core.runtime.IExtension;
 import org.eclipse.core.runtime.IExtensionDelta;
 import org.eclipse.core.runtime.IExtensionPoint;
 import org.eclipse.core.runtime.IExtensionRegistry;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IProduct;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IRegistryChangeEvent;
 import org.eclipse.core.runtime.IRegistryChangeListener;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.ListenerList;
 import org.eclipse.core.runtime.MultiStatus;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.SafeRunner;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.jface.action.ActionContributionItem;
 import org.eclipse.jface.action.ExternalActionManager;
 import org.eclipse.jface.action.IAction;
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.action.ExternalActionManager.CommandCallback;
 import org.eclipse.jface.action.ExternalActionManager.IActiveChecker;
 import org.eclipse.jface.bindings.BindingManager;
 import org.eclipse.jface.bindings.BindingManagerEvent;
 import org.eclipse.jface.bindings.IBindingManagerListener;
 import org.eclipse.jface.databinding.swt.SWTObservables;
 import org.eclipse.jface.dialogs.ErrorDialog;
 import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
 import org.eclipse.jface.operation.IRunnableContext;
 import org.eclipse.jface.operation.ModalContext;
 import org.eclipse.jface.preference.IPreferenceStore;
 import org.eclipse.jface.preference.PreferenceManager;
 import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.util.OpenStrategy;
 import org.eclipse.jface.util.SafeRunnable;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.window.IShellProvider;
 import org.eclipse.jface.window.Window;
 import org.eclipse.jface.window.WindowManager;
 import org.eclipse.osgi.service.runnable.StartupMonitor;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.BusyIndicator;
 import org.eclipse.swt.graphics.DeviceData;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.ui.IDecoratorManager;
 import org.eclipse.ui.IEditorPart;
 import org.eclipse.ui.IEditorRegistry;
 import org.eclipse.ui.IElementFactory;
 import org.eclipse.ui.ILocalWorkingSetManager;
 import org.eclipse.ui.IMemento;
 import org.eclipse.ui.IPerspectiveDescriptor;
 import org.eclipse.ui.IPerspectiveRegistry;
 import org.eclipse.ui.ISaveableFilter;
 import org.eclipse.ui.ISaveablePart;
 import org.eclipse.ui.ISaveablesLifecycleListener;
 import org.eclipse.ui.ISharedImages;
 import org.eclipse.ui.IWindowListener;
 import org.eclipse.ui.IWorkbench;
 import org.eclipse.ui.IWorkbenchListener;
 import org.eclipse.ui.IWorkbenchPage;
 import org.eclipse.ui.IWorkbenchPart;
 import org.eclipse.ui.IWorkbenchPreferenceConstants;
 import org.eclipse.ui.IWorkbenchWindow;
 import org.eclipse.ui.IWorkingSetManager;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.Saveable;
 import org.eclipse.ui.WorkbenchException;
 import org.eclipse.ui.XMLMemento;
 import org.eclipse.ui.activities.IWorkbenchActivitySupport;
 import org.eclipse.ui.application.IWorkbenchConfigurer;
 import org.eclipse.ui.application.WorkbenchAdvisor;
 import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
 import org.eclipse.ui.commands.ICommandService;
 import org.eclipse.ui.commands.IWorkbenchCommandSupport;
 import org.eclipse.ui.contexts.IContextService;
 import org.eclipse.ui.contexts.IWorkbenchContextSupport;
 import org.eclipse.ui.handlers.IHandlerService;
 import org.eclipse.ui.help.IWorkbenchHelpSystem;
 import org.eclipse.ui.internal.StartupThreading.StartupRunnable;
 import org.eclipse.ui.internal.activities.ws.WorkbenchActivitySupport;
 import org.eclipse.ui.internal.browser.WorkbenchBrowserSupport;
 import org.eclipse.ui.internal.commands.CommandImageManager;
 import org.eclipse.ui.internal.commands.CommandImageService;
 import org.eclipse.ui.internal.commands.CommandService;
 import org.eclipse.ui.internal.commands.ICommandImageService;
 import org.eclipse.ui.internal.commands.WorkbenchCommandSupport;
 import org.eclipse.ui.internal.contexts.ActiveContextSourceProvider;
 import org.eclipse.ui.internal.contexts.ContextService;
 import org.eclipse.ui.internal.contexts.WorkbenchContextSupport;
 import org.eclipse.ui.internal.dialogs.PropertyPageContributorManager;
 import org.eclipse.ui.internal.handlers.HandlerService;
 import org.eclipse.ui.internal.help.WorkbenchHelpSystem;
 import org.eclipse.ui.internal.intro.IIntroRegistry;
 import org.eclipse.ui.internal.intro.IntroDescriptor;
 import org.eclipse.ui.internal.keys.BindingService;
 import org.eclipse.ui.internal.menus.FocusControlSourceProvider;
 import org.eclipse.ui.internal.menus.WorkbenchMenuService;
 import org.eclipse.ui.internal.misc.Policy;
 import org.eclipse.ui.internal.misc.StatusUtil;
 import org.eclipse.ui.internal.misc.UIStats;
 import org.eclipse.ui.internal.progress.ProgressManager;
 import org.eclipse.ui.internal.registry.IActionSetDescriptor;
 import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
 import org.eclipse.ui.internal.registry.UIExtensionTracker;
 import org.eclipse.ui.internal.services.ActionSetSourceProvider;
 import org.eclipse.ui.internal.services.ActivePartSourceProvider;
 import org.eclipse.ui.internal.services.ActiveShellSourceProvider;
 import org.eclipse.ui.internal.services.CurrentSelectionSourceProvider;
 import org.eclipse.ui.internal.services.EvaluationService;
 import org.eclipse.ui.internal.services.IEvaluationService;
 import org.eclipse.ui.internal.services.ISourceProviderService;
 import org.eclipse.ui.internal.services.MenuSourceProvider;
 import org.eclipse.ui.internal.services.ServiceLocator;
 import org.eclipse.ui.internal.services.SourceProviderService;
 import org.eclipse.ui.internal.splash.EclipseSplashHandler;
 import org.eclipse.ui.internal.splash.SplashHandlerFactory;
 import org.eclipse.ui.internal.testing.WorkbenchTestable;
 import org.eclipse.ui.internal.themes.ColorDefinition;
 import org.eclipse.ui.internal.themes.FontDefinition;
 import org.eclipse.ui.internal.themes.ThemeElementHelper;
 import org.eclipse.ui.internal.themes.WorkbenchThemeManager;
 import org.eclipse.ui.internal.tweaklets.GrabFocus;
 import org.eclipse.ui.internal.tweaklets.Tweaklets;
 import org.eclipse.ui.internal.util.PrefUtil;
 import org.eclipse.ui.internal.util.Util;
 import org.eclipse.ui.intro.IIntroManager;
 import org.eclipse.ui.keys.IBindingService;
 import org.eclipse.ui.menus.IMenuService;
 import org.eclipse.ui.operations.IWorkbenchOperationSupport;
 import org.eclipse.ui.progress.IProgressService;
 import org.eclipse.ui.services.IDisposable;
 import org.eclipse.ui.splash.AbstractSplashHandler;
 import org.eclipse.ui.statushandlers.StatusManager;
 import org.eclipse.ui.swt.IFocusService;
 import org.eclipse.ui.themes.IThemeManager;
 import org.eclipse.ui.views.IViewRegistry;
 import org.eclipse.ui.wizards.IWizardRegistry;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleEvent;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.framework.SynchronousBundleListener;

 /**
  * The workbench class represents the top of the Eclipse user interface. Its
  * primary responsability is the management of workbench windows, dialogs,
  * wizards, and other workbench-related windows.
  * <p>
  * Note that any code that is run during the creation of a workbench instance
  * should not required access to the display.
  * </p>
  * <p>
  * Note that this internal class changed significantly between 2.1 and 3.0.
  * Applications that used to define subclasses of this internal class need to be
  * rewritten to use the new workbench advisor API.
  * </p>
  */
 public final class Workbench extends EventManager implements IWorkbench {

     private final class StartupProgressBundleListener implements
             SynchronousBundleListener {

         private final IProgressMonitor progressMonitor;

         private final int maximumProgressCount;

         // stack of names of bundles currently starting
 private final List starting;

         StartupProgressBundleListener(IProgressMonitor progressMonitor,
                 int maximumProgressCount) {
             super();
             this.progressMonitor = progressMonitor;
             this.maximumProgressCount = maximumProgressCount;
             this.starting = new ArrayList ();
         }

         public void bundleChanged(BundleEvent event) {
             int eventType = event.getType();
             String bundleName;

             synchronized (this) {
                 if (eventType == BundleEvent.STARTING) {
                     starting.add(bundleName = event.getBundle()
                             .getSymbolicName());
                 } else if (eventType == BundleEvent.STARTED) {
                     progressCount++;
                     if (progressCount <= maximumProgressCount) {
                         progressMonitor.worked(1);
                     }
                     int index = starting.lastIndexOf(event.getBundle()
                             .getSymbolicName());
                     if (index >= 0) {
                         starting.remove(index);
                     }
                     if (index != starting.size()) {
                         return; // not currently displayed
 }
                     bundleName = index == 0 ? null : (String ) starting
                             .get(index - 1);
                 } else {
                     return; // uninteresting event
 }
             }

             String taskName;

             if (bundleName == null) {
                 taskName = WorkbenchMessages.Startup_Loading_Workbench;
             } else {
                 taskName = NLS.bind(WorkbenchMessages.Startup_Loading,
                         bundleName);
             }

             progressMonitor.subTask(taskName);
         }
     }

     /**
      * Family for the early startup job.
      */
     public static final String EARLY_STARTUP_FAMILY = "earlyStartup"; //$NON-NLS-1$

     static final String VERSION_STRING[] = { "0.046", "2.0" }; //$NON-NLS-1$ //$NON-NLS-2$

     static final String DEFAULT_WORKBENCH_STATE_FILENAME = "workbench.xml"; //$NON-NLS-1$

     /**
      * Holds onto the only instance of Workbench.
      */
     private static Workbench instance;

     /**
      * The testable object facade.
      *
      * @since 3.0
      */
     private static WorkbenchTestable testableObject;

     /**
      * Signals that the workbench should create a splash implementation when
      * instantiated. Intial value is <code>true</code>.
      *
      * @since 3.3
      */
     private static boolean createSplash = true;

     /**
      * The splash handler.
      */
     private static AbstractSplashHandler splash;

     /**
      * The display used for all UI interactions with this workbench.
      *
      * @since 3.0
      */
     private Display display;

     private WindowManager windowManager;

     private WorkbenchWindow activatedWindow;

     private EditorHistory editorHistory;

     private boolean runEventLoop = true;

     private boolean isStarting = true;

     private boolean isClosing = false;

     /**
      * PlatformUI return code (as opposed to IPlatformRunnable return code).
      */
     private int returnCode = PlatformUI.RETURN_UNSTARTABLE;

     /**
      * Advisor providing application-specific configuration and customization of
      * the workbench.
      *
      * @since 3.0
      */
     private WorkbenchAdvisor advisor;

     /**
      * Object for configuring the workbench. Lazily initialized to an instance
      * unique to the workbench instance.
      *
      * @since 3.0
      */
     private WorkbenchConfigurer workbenchConfigurer;

     // for dynamic UI
 /**
      * ExtensionEventHandler handles extension life-cycle events.
      */
     private ExtensionEventHandler extensionEventHandler;

     /**
      * A count of how many large updates are going on. This tracks nesting of
      * requests to disable services during a large update -- similar to the
      * <code>setRedraw</code> functionality on <code>Control</code>. When
      * this value becomes greater than zero, services are disabled. When this
      * value becomes zero, services are enabled. Please see
      * <code>largeUpdateStart()</code> and <code>largeUpdateEnd()</code>.
      */
     private int largeUpdates = 0;

     /**
      * The service locator maintained by the workbench. These services are
      * initialized during workbench during the <code>init</code> method.
      */
     private final ServiceLocator serviceLocator = new ServiceLocator();

     /**
      * A count of how many plug-ins were loaded while restoring the workbench
      * state. Initially -1 for unknown number.
      */
     private int progressCount = -1;

     /**
      * A field to hold the workbench windows that have been restored. In the
      * event that not all windows have been restored, this field allows the
      * openWindowsAfterRestore method to open some windows.
      */
     private WorkbenchWindow[] createdWindows;

     /**
      * Listener list for registered IWorkbenchListeners .
      */
     private ListenerList workbenchListeners = new ListenerList(
             ListenerList.IDENTITY);

     /**
      * Creates a new workbench.
      *
      * @param display
      * the display to be used for all UI interactions with the
      * workbench
      * @param advisor
      * the application-specific advisor that configures and
      * specializes this workbench instance
      * @since 3.0
      */
     private Workbench(Display display, WorkbenchAdvisor advisor) {
         super();
         StartupThreading.setWorkbench(this);
         if (instance != null && instance.isRunning()) {
             throw new IllegalStateException (
                     WorkbenchMessages.Workbench_CreatingWorkbenchTwice);
         }
         Assert.isNotNull(display);
         Assert.isNotNull(advisor);
         this.advisor = advisor;
         this.display = display;
         Workbench.instance = this;

         // for dynamic UI [This seems to be for everything that isn't handled by
 // some
 // subclass of RegistryManager. I think that when an extension is moved
 // to the
 // RegistryManager implementation, then it should be removed from the
 // list in
 // ExtensionEventHandler#appear.
 // I've found that the new wizard extension in particular is a poor
 // choice to
 // use as an example, since the result of reading the registry is not
 // cached
 // -- so it is re-read each time. The only real contribution of this
 // dialog is
 // to show the user a nice dialog describing the addition.]
 extensionEventHandler = new ExtensionEventHandler(this);
         Platform.getExtensionRegistry().addRegistryChangeListener(
                 extensionEventHandler);
     }

     /**
      * Returns the one and only instance of the workbench, if there is one.
      *
      * @return the workbench, or <code>null</code> if the workbench has not
      * been created, or has been created and already completed
      */
     public static final Workbench getInstance() {
         return instance;
     }

     /**
      * Creates the workbench and associates it with the the given display and
      * workbench advisor, and runs the workbench UI. This entails processing and
      * dispatching events until the workbench is closed or restarted.
      * <p>
      * This method is intended to be called by <code>PlatformUI</code>. Fails
      * if the workbench UI has already been created.
      * </p>
      * <p>
      * The display passed in must be the default display.
      * </p>
      *
      * @param display
      * the display to be used for all UI interactions with the
      * workbench
      * @param advisor
      * the application-specific advisor that configures and
      * specializes the workbench
      * @return return code {@link PlatformUI#RETURN_OK RETURN_OK}for normal
      * exit; {@link PlatformUI#RETURN_RESTART RETURN_RESTART}if the
      * workbench was terminated with a call to
      * {@link IWorkbench#restart IWorkbench.restart}; other values
      * reserved for future use
      */
     public static final int createAndRunWorkbench(final Display display,
             final WorkbenchAdvisor advisor) {
         final int[] returnCode = new int[1];
         Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable () {
             public void run() {
                 // create the workbench instance
 Workbench workbench = new Workbench(display, advisor);
                 // run the workbench event loop
 returnCode[0] = workbench.runUI();
             }
         });
         return returnCode[0];
     }

     /**
      * Creates the <code>Display</code> to be used by the workbench.
      *
      * @return the display
      */
     public static Display createDisplay() {
         // setup the application name used by SWT to lookup resources on some
 // platforms
 String applicationName = WorkbenchPlugin.getDefault().getAppName();
         if (applicationName != null) {
             Display.setAppName(applicationName);
         }

         // create the display
 Display newDisplay = Display.getCurrent();
         if(newDisplay == null) {
             if (Policy.DEBUG_SWT_GRAPHICS || Policy.DEBUG_SWT_DEBUG) {
                 DeviceData data = new DeviceData();
                 if (Policy.DEBUG_SWT_GRAPHICS) {
                     data.tracking = true;
                 }
                 if (Policy.DEBUG_SWT_DEBUG) {
                     data.debug = true;
                 }
                 newDisplay = new Display(data);
             } else {
                 newDisplay = new Display();
             }
         }

         // workaround for 1GEZ9UR and 1GF07HN
 newDisplay.setWarnings(false);

         // Set the priority higher than normal so as to be higher
 // than the JobManager.
 Thread.currentThread().setPriority(
                 Math.min(Thread.MAX_PRIORITY, Thread.NORM_PRIORITY + 1));

         initializeImages();

         return newDisplay;
     }

     /**
      * Create the splash wrapper and set it to work.
      *
      * @since 3.3
      */
     private void createSplashWrapper() {
         final Display display = getDisplay();
         String splashLoc = System.getProperty("org.eclipse.equinox.launcher.splash.location"); //$NON-NLS-1$
 final Image background = loadImage(splashLoc);
         
         SafeRunnable run = new SafeRunnable() {

             public void run() throws Exception {
                 String splashHandle = System.getProperty("org.eclipse.equinox.launcher.splash.handle"); //$NON-NLS-1$
 if (splashHandle == null) {
                     createSplash = false;
                     return;
                 }
                 
                 // create the splash
 getSplash();
                 if (splash == null) {
                     createSplash = false;
                     return;
                 }
                 
                 Shell splashShell = splash.getSplash();
                 if (splashShell == null) {
                     // look for the 32 bit internal_new shell method
 try {
                         Method method = Shell.class
                                 .getMethod(
                                         "internal_new", new Class [] { Display.class, int.class }); //$NON-NLS-1$
 // we're on a 32 bit platform so invoke it with splash
 // handle as an int
 splashShell = (Shell) method.invoke(null, new Object [] {
                                 display, new Integer (splashHandle) });
                     } catch (NoSuchMethodException e) {
                         // look for the 64 bit internal_new shell method
 try {
                             Method method = Shell.class
                                     .getMethod(
                                             "internal_new", new Class [] { Display.class, long.class }); //$NON-NLS-1$

                             // we're on a 64 bit platform so invoke it with a long
 splashShell = (Shell) method.invoke(null,
                                     new Object [] { display,
                                             new Long (splashHandle) });
                         } catch (NoSuchMethodException e2) {
                             // cant find either method - don't do anything.
 }
                     }
                     
                     if (splashShell == null)
                         return;
                     if (background != null)
                         splashShell.setBackgroundImage(background);
                 }

                 Dictionary properties = new Hashtable ();
                 properties.put(Constants.SERVICE_RANKING, new Integer (Integer.MAX_VALUE));
                 BundleContext context = WorkbenchPlugin.getDefault().getBundleContext();
                 final ServiceRegistration registration[] = new ServiceRegistration[1];
                 StartupMonitor startupMonitor = new StartupMonitor() {

                     public void applicationRunning() {
                         splash.dispose();
                         if (background != null)
                             background.dispose();
                         registration[0].unregister(); // unregister ourself
 }

                     public void update() {
                         // do nothing - we come into the picture far too late
 // for this to be relevant
 }
                 };
                 registration[0] = context.registerService(StartupMonitor.class
                         .getName(), startupMonitor, properties);
                 
                 splash.init(splashShell);
             }
             /* (non-Javadoc)
              * @see org.eclipse.jface.util.SafeRunnable#handleException(java.lang.Throwable)
              */
             public void handleException(Throwable e) {
                 StatusManager.getManager().handle(
                         StatusUtil.newStatus(WorkbenchPlugin.PI_WORKBENCH,
                                 "Could not instantiate splash", e)); //$NON-NLS-1$
 createSplash = false;
                 splash = null;
                 if (background != null)
                     background.dispose();
                 
             }
             };
             SafeRunner.run(run);
     }

     /**
      * Load an image from a filesystem path.
      *
      * @param splashLoc the location to load from
      * @return the image or <code>null</code>
      * @since 3.3
      */
     private Image loadImage(String splashLoc) {
         Image background = null;
         if (splashLoc != null) {
             try {
                 InputStream input = new BufferedInputStream (
                         new FileInputStream (splashLoc));
                 background = new Image(display, input);
             } catch (IOException e) {
                 StatusManager.getManager().handle(
                         StatusUtil.newStatus(WorkbenchPlugin.PI_WORKBENCH, e));
             }
         }
         return background;
     }

     /**
      * Return the splash handler for this application. If none is specifically
      * provided the default Eclipse implementation is returned.
      *
      * @return the splash handler for this application or <code>null</code>
      * @since 3.3
      */
     private static AbstractSplashHandler getSplash() {
         if (!createSplash)
             return null;
         
         if (splash == null) {
             
             IProduct product = Platform.getProduct();
             if (product != null)
                 splash = SplashHandlerFactory.findSplashHandlerFor(product);
             
             if (splash == null)
                 splash = new EclipseSplashHandler();
         }
         return splash;
     }

     /**
      * Returns the testable object facade, for use by the test harness.
      *
      * @return the testable object facade
      * @since 3.0
      */
     public static WorkbenchTestable getWorkbenchTestable() {
         if (testableObject == null) {
             testableObject = new WorkbenchTestable();
         }
         return testableObject;
     }

     /*
      * (non-Javadoc) Method declared on IWorkbench.
      *
      * @since 3.2
      */
     public void addWorkbenchListener(IWorkbenchListener listener) {
         workbenchListeners.add(listener);
     }

     /*
      * (non-Javadoc) Method declared on IWorkbench.
      *
      * @since 3.2
      */
     public void removeWorkbenchListener(IWorkbenchListener listener) {
         workbenchListeners.remove(listener);
     }

     /**
      * Fire workbench preShutdown event, stopping at the first one to veto
      *
      * @param forced
      * flag indicating whether the shutdown is being forced
      * @return <code>true</code> to allow the workbench to proceed with
      * shutdown, <code>false</code> to veto a non-forced shutdown
      * @since 3.2
      */
     boolean firePreShutdown(final boolean forced) {
         Object list[] = workbenchListeners.getListeners();
         for (int i = 0; i < list.length; i++) {
             final IWorkbenchListener l = (IWorkbenchListener) list[i];
             final boolean[] result = new boolean[] { false };
             SafeRunnable.run(new SafeRunnable() {
                 public void run() {
                     result[0] = l.preShutdown(Workbench.this, forced);
                 }
             });
             if (!result[0]) {
                 return false;
             }
         }
         return true;
     }

     /**
      * Fire workbench postShutdown event.
      *
      * @since 3.2
      */
     void firePostShutdown() {
         Object list[] = workbenchListeners.getListeners();
         for (int i = 0; i < list.length; i++) {
             final IWorkbenchListener l = (IWorkbenchListener) list[i];
             SafeRunnable.run(new SafeRunnable() {
                 public void run() {
                     l.postShutdown(Workbench.this);
                 }
             });
         }
     }

     /*
      * (non-Javadoc) Method declared on IWorkbench.
      */
     public void addWindowListener(IWindowListener l) {
         addListenerObject(l);
     }

     /*
      * (non-Javadoc) Method declared on IWorkbench.
      */
     public void removeWindowListener(IWindowListener l) {
         removeListenerObject(l);
     }

     /**
      * Fire window opened event.
      *
      * @param window
      * The window which just opened; should not be <code>null</code>.
      */
     protected void fireWindowOpened(final IWorkbenchWindow window) {
         Object list[] = getListeners();
         for (int i = 0; i < list.length; i++) {
             final IWindowListener l = (IWindowListener) list[i];
             SafeRunner.run(new SafeRunnable() {
                 public void run() {
                     l.windowOpened(window);
                 }
             });
         }
     }

     /**
      * Fire window closed event.
      *
      * @param window
      * The window which just closed; should not be <code>null</code>.
      */
     protected void fireWindowClosed(final IWorkbenchWindow window) {
         if (activatedWindow == window) {
             // Do not hang onto it so it can be GC'ed
 activatedWindow = null;
         }

         Object list[] = getListeners();
         for (int i = 0; i < list.length; i++) {
             final IWindowListener l = (IWindowListener) list[i];
             SafeRunner.run(new SafeRunnable() {
                 public void run() {
                     l.windowClosed(window);
                 }
             });
         }
     }

     /**
      * Fire window activated event.
      *
      * @param window
      * The window which was just activated; should not be
      * <code>null</code>.
      */
     protected void fireWindowActivated(final IWorkbenchWindow window) {
         Object list[] = getListeners();
         for (int i = 0; i < list.length; i++) {
             final IWindowListener l = (IWindowListener) list[i];
             SafeRunner.run(new SafeRunnable() {
                 public void run() {
                     l.windowActivated(window);
                 }
             });
         }
     }

     /**
      * Fire window deactivated event.
      *
      * @param window
      * The window which was just deactivated; should not be
      * <code>null</code>.
      */
     protected void fireWindowDeactivated(final IWorkbenchWindow window) {
         Object list[] = getListeners();
         for (int i = 0; i < list.length; i++) {
             final IWindowListener l = (IWindowListener) list[i];
             SafeRunner.run(new SafeRunnable() {
                 public void run() {
                     l.windowDeactivated(window);
                 }
             });
         }
     }

     /**
      * Closes the workbench. Assumes that the busy cursor is active.
      *
      * @param force
      * true if the close is mandatory, and false if the close is
      * allowed to fail
      * @return true if the close succeeded, and false otherwise
      */
     private boolean busyClose(final boolean force) {

         // notify the advisor of preShutdown and allow it to veto if not forced
 isClosing = advisor.preShutdown();
         if (!force && !isClosing) {
             return false;
         }

         // notify regular workbench clients of preShutdown and allow them to
 // veto if not forced
 isClosing = firePreShutdown(force);
         if (!force && !isClosing) {
             return false;
         }

         // save any open editors if they are dirty
 isClosing = saveAllEditors(!force);
         if (!force && !isClosing) {
             return false;
         }

         boolean closeEditors = !force
                 && PrefUtil.getAPIPreferenceStore().getBoolean(
                         IWorkbenchPreferenceConstants.CLOSE_EDITORS_ON_EXIT);
         if (closeEditors) {
             SafeRunner.run(new SafeRunnable() {
                 public void run() {
                     IWorkbenchWindow windows[] = getWorkbenchWindows();
                     for (int i = 0; i < windows.length; i++) {
                         IWorkbenchPage pages[] = windows[i].getPages();
                         for (int j = 0; j < pages.length; j++) {
                             isClosing = isClosing
                                     && pages[j].closeAllEditors(false);
                         }
                     }
                 }
             });
             if (!force && !isClosing) {
                 return false;
             }
         }

         if (getWorkbenchConfigurer().getSaveAndRestore()) {
             SafeRunner.run(new SafeRunnable() {
                 public void run() {
                     XMLMemento mem = recordWorkbenchState();
                     // Save the IMemento to a file.
 saveMementoToFile(mem);
                 }

                 public void handleException(Throwable e) {
                     String message;
                     if (e.getMessage() == null) {
                         message = WorkbenchMessages.ErrorClosingNoArg;
                     } else {
                         message = NLS.bind(
                                 WorkbenchMessages.ErrorClosingOneArg, e
                                         .getMessage());
                     }

                     if (!MessageDialog.openQuestion(null,
                             WorkbenchMessages.Error, message)) {
                         isClosing = false;
                     }
                 }
             });
         }
         if (!force && !isClosing) {
             return false;
         }

         SafeRunner.run(new SafeRunnable(WorkbenchMessages.ErrorClosing) {
             public void run() {
                 if (isClosing || force) {
                     isClosing = windowManager.close();
                 }
             }
         });

         if (!force && !isClosing) {
             return false;
         }

         shutdown();

         runEventLoop = false;
         return true;
     }

     
     /* (non-Javadoc)
      * @see org.eclipse.ui.IWorkbench#saveAllEditors(boolean)
      */
     public boolean saveAllEditors(boolean confirm) {
         final boolean finalConfirm = confirm;
         final boolean[] result = new boolean[1];
         result[0] = true;

         SafeRunner.run(new SafeRunnable(WorkbenchMessages.ErrorClosing) {
             public void run() {
                 // Collect dirtyParts
 ArrayList dirtyParts = new ArrayList ();
                 ArrayList dirtyEditorsInput = new ArrayList ();
                 IWorkbenchWindow windows[] = getWorkbenchWindows();
                 for (int i = 0; i < windows.length; i++) {
                     IWorkbenchPage pages[] = windows[i].getPages();
                     for (int j = 0; j < pages.length; j++) {
                         WorkbenchPage page = (WorkbenchPage) pages[j];

                         ISaveablePart[] parts = page.getDirtyParts();

                         for (int k = 0; k < parts.length; k++) {
                             ISaveablePart part = parts[k];

                             if (part.isSaveOnCloseNeeded()) {
                                 if (part instanceof IEditorPart) {
                                     IEditorPart editor = (IEditorPart) part;
                                     if (!dirtyEditorsInput.contains(editor
                                             .getEditorInput())) {
                                         dirtyParts.add(editor);
                                         dirtyEditorsInput.add(editor
                                                 .getEditorInput());
                                     }
                                 } else {
                                     dirtyParts.add(part);
                                 }
                             }
                         }
                     }
                 }
                 IShellProvider shellProvider;
                 IRunnableContext runnableContext;
                 IWorkbenchWindow w = getActiveWorkbenchWindow();
                 if (w == null && windows.length > 0) {
                     w = windows[0];
                 }
                 if (w != null) {
                     shellProvider = (WorkbenchWindow)w;
                     runnableContext = w;
                 } else {
                     shellProvider = new IShellProvider() {
                         public Shell getShell() {
                             return null;
                         }
                     };
                     runnableContext = new ProgressMonitorDialog(null);
                 }
                 // The fourth parameter is true to also save saveables from
 // non-part sources, see bug 139004.
 result[0] = EditorManager.saveAll(dirtyParts, finalConfirm,
                         false, true, runnableContext, shellProvider);
             }
         });
         return result[0];
     }

     /**
      * Opens a new workbench window and page with a specific perspective.
      *
      * Assumes that busy cursor is active.
      */
     private IWorkbenchWindow busyOpenWorkbenchWindow(final String perspID,
             final IAdaptable input) throws WorkbenchException {
         // Create a workbench window (becomes active window)
 final WorkbenchWindow newWindowArray[] = new WorkbenchWindow[1];
         StartupThreading.runWithWorkbenchExceptions(new StartupRunnable() {
            public void runWithException() {
                newWindowArray[0] = newWorkbenchWindow();
            }
        });

        final WorkbenchWindow newWindow = newWindowArray[0];
        
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                newWindow.create(); // must be created before adding to window
 // manager
 }
        });
        windowManager.add(newWindow);

        final WorkbenchException [] exceptions = new WorkbenchException[1];
        // Create the initial page.
 if (perspID != null) {
            StartupThreading.runWithWorkbenchExceptions(new StartupRunnable() {

                public void runWithException() throws WorkbenchException {
                    try {
                        newWindow.busyOpenPage(perspID, input);
                    } catch (WorkbenchException e) {
                        windowManager.remove(newWindow);
                        exceptions[0] = e;
                    }
                }});
        }
        if (exceptions[0] != null)
            throw exceptions[0];

        // Open window after opening page, to avoid flicker.
 StartupThreading.runWithWorkbenchExceptions(new StartupRunnable() {

            public void runWithException() {
                newWindow.open();
            }
        });

        return newWindow;
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public boolean close() {
        return close(PlatformUI.RETURN_OK, false);
    }

    /**
     * Closes the workbench, returning the given return code from the run
     * method. If forced, the workbench is closed no matter what.
     *
     * @param returnCode
     * {@link PlatformUI#RETURN_OK RETURN_OK}for normal exit;
     * {@link PlatformUI#RETURN_RESTART RETURN_RESTART}if the
     * workbench was terminated with a call to
     * {@link IWorkbench#restart IWorkbench.restart};
     * {@link PlatformUI#RETURN_EMERGENCY_CLOSE} for an emergency
     * shutdown
     * {@link PlatformUI#RETURN_UNSTARTABLE RETURN_UNSTARTABLE}if
     * the workbench could not be started; other values reserved for
     * future use
     *
     * @param force
     * true to force the workbench close, and false for a "soft"
     * close that can be canceled
     * @return true if the close was successful, and false if the close was
     * canceled
     */
    /* package */
    boolean close(int returnCode, final boolean force) {
        this.returnCode = returnCode;
        final boolean[] ret = new boolean[1];
        BusyIndicator.showWhile(null, new Runnable () {
            public void run() {
                ret[0] = busyClose(force);
            }
        });
        return ret[0];
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public IWorkbenchWindow getActiveWorkbenchWindow() {
        // Return null if called from a non-UI thread.
 // This is not spec'ed behaviour and is misleading, however this is how
 // it
 // worked in 2.1 and we cannot change it now.
 // For more details, see [Bug 57384] [RCP] Main window not active on
 // startup
 if (Display.getCurrent() == null) {
            return null;
        }

        // Look at the current shell and up its parent
 // hierarchy for a workbench window.
 Control shell = display.getActiveShell();
        while (shell != null) {
            Object data = shell.getData();
            if (data instanceof IWorkbenchWindow) {
                return (IWorkbenchWindow) data;
            }
            shell = shell.getParent();
        }

        // Look for the window that was last known being
 // the active one
 WorkbenchWindow win = getActivatedWindow();
        if (win != null) {
            return win;
        }

        // Look at all the shells and pick the first one
 // that is a workbench window.
 Shell shells[] = display.getShells();
        for (int i = 0; i < shells.length; i++) {
            Object data = shells[i].getData();
            if (data instanceof IWorkbenchWindow) {
                return (IWorkbenchWindow) data;
            }
        }

        // Can't find anything!
 return null;
    }

    /*
     * Returns the editor history.
     */
    public EditorHistory getEditorHistory() {
        if (editorHistory == null) {
            editorHistory = new EditorHistory();
        }
        return editorHistory;
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public IEditorRegistry getEditorRegistry() {
        return WorkbenchPlugin.getDefault().getEditorRegistry();
    }

    /*
     * Returns the number for a new window. This will be the first number > 0
     * which is not used to identify another window in the workbench.
     */
    private int getNewWindowNumber() {
        // Get window list.
 Window[] windows = windowManager.getWindows();
        int count = windows.length;

        // Create an array of booleans (size = window count).
 // Cross off every number found in the window list.
 boolean checkArray[] = new boolean[count];
        for (int nX = 0; nX < count; nX++) {
            if (windows[nX] instanceof WorkbenchWindow) {
                WorkbenchWindow ww = (WorkbenchWindow) windows[nX];
                int index = ww.getNumber() - 1;
                if (index >= 0 && index < count) {
                    checkArray[index] = true;
                }
            }
        }

        // Return first index which is not used.
 // If no empty index was found then every slot is full.
 // Return next index.
 for (int index = 0; index < count; index++) {
            if (!checkArray[index]) {
                return index + 1;
            }
        }
        return count + 1;
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public IWorkbenchOperationSupport getOperationSupport() {
        return WorkbenchPlugin.getDefault().getOperationSupport();
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public IPerspectiveRegistry getPerspectiveRegistry() {
        return WorkbenchPlugin.getDefault().getPerspectiveRegistry();
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public PreferenceManager getPreferenceManager() {
        return WorkbenchPlugin.getDefault().getPreferenceManager();
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public IPreferenceStore getPreferenceStore() {
        return WorkbenchPlugin.getDefault().getPreferenceStore();
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public ISharedImages getSharedImages() {
        return WorkbenchPlugin.getDefault().getSharedImages();
    }

    /**
     * Returns the window manager for this workbench.
     *
     * @return the window manager
     */
    /* package */
    WindowManager getWindowManager() {
        return windowManager;
    }

    /*
     * Answer the workbench state file.
     */
    private File getWorkbenchStateFile() {
        IPath path = WorkbenchPlugin.getDefault().getDataLocation();
        if (path == null) {
            return null;
        }
        path = path.append(DEFAULT_WORKBENCH_STATE_FILENAME);
        return path.toFile();
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public int getWorkbenchWindowCount() {
        return windowManager.getWindowCount();
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public IWorkbenchWindow[] getWorkbenchWindows() {
        Window[] windows = windowManager.getWindows();
        IWorkbenchWindow[] dwindows = new IWorkbenchWindow[windows.length];
        System.arraycopy(windows, 0, dwindows, 0, windows.length);
        return dwindows;
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public IWorkingSetManager getWorkingSetManager() {
        return WorkbenchPlugin.getDefault().getWorkingSetManager();
    }

    /**
     * {@inheritDoc}
     */
    public ILocalWorkingSetManager createLocalWorkingSetManager() {
        return new LocalWorkingSetManager(WorkbenchPlugin.getDefault()
                .getBundleContext());
    }

    /**
     * Initializes the workbench now that the display is created.
     *
     * @return true if init succeeded.
     */
    private boolean init() {
        // setup debug mode if required.
 if (WorkbenchPlugin.getDefault().isDebugging()) {
            WorkbenchPlugin.DEBUG = true;
            ModalContext.setDebugMode(true);
        }

        // Set up the JFace preference store
 JFaceUtil.initializeJFacePreferences();

        // create workbench window manager
 windowManager = new WindowManager();

        IIntroRegistry introRegistry = WorkbenchPlugin.getDefault()
                .getIntroRegistry();
        if (introRegistry.getIntroCount() > 0) {
            IProduct product = Platform.getProduct();
            if (product != null) {
                introDescriptor = (IntroDescriptor) introRegistry
                        .getIntroForProduct(product.getId());
            }
        }

        // Initialize the activity support.
 workbenchActivitySupport = new WorkbenchActivitySupport();
        activityHelper = ActivityPersistanceHelper.getInstance();

        initializeDefaultServices();
        initializeFonts();
        initializeColors();
        initializeApplicationColors();

        // now that the workbench is sufficiently initialized, let the advisor
 // have a turn.
 StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                advisor.internalBasicInitialize(getWorkbenchConfigurer());
            }
        });
        
        // configure use of color icons in toolbars
 boolean useColorIcons = PrefUtil.getInternalPreferenceStore()
                .getBoolean(IPreferenceConstants.COLOR_ICONS);
        ActionContributionItem.setUseColorIconsInToolbars(useColorIcons);

        // initialize workbench single-click vs double-click behavior
 initializeSingleClickOption();
        
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                ((GrabFocus) Tweaklets.get(GrabFocus.KEY))
                        .init(getDisplay());
            }
        });
        

        // attempt to restore a previous workbench state
 try {
            UIStats.start(UIStats.RESTORE_WORKBENCH, "Workbench"); //$NON-NLS-1$

            final boolean bail [] = new boolean[1];
            StartupThreading.runWithoutExceptions(new StartupRunnable() {

                public void runWithException() throws Throwable {
                    advisor.preStartup();
                    
                    if (!advisor.openWindows()) {
                        bail[0] = true;
                    }
                }});
            
            if (bail[0])
                return false;

        } finally {
            UIStats.end(UIStats.RESTORE_WORKBENCH, this, "Workbench"); //$NON-NLS-1$
 }

        forceOpenPerspective();

        return true;
    }

    /**
     * Establishes the relationship between JFace actions and the command
     * manager.
     */
    private void initializeCommandResolver() {
        ExternalActionManager.getInstance().setCallback(
                new CommandCallback(bindingManager, commandManager,
                        new IActiveChecker() {
                            public final boolean isActive(final String commandId) {
                                return workbenchActivitySupport
                                        .getActivityManager().getIdentifier(
                                                commandId).isEnabled();
                            }
                        }));
    }

    /**
     * Initialize colors defined by the new colorDefinitions extension point.
     * Note this will be rolled into initializeColors() at some point.
     *
     * @since 3.0
     */
    private void initializeApplicationColors() {
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                ColorDefinition[] colorDefinitions = WorkbenchPlugin
                        .getDefault().getThemeRegistry().getColors();
                ThemeElementHelper.populateRegistry(getThemeManager().getTheme(
                        IThemeManager.DEFAULT_THEME), colorDefinitions,
                        PrefUtil.getInternalPreferenceStore());
            }
        });
    }

    private void initializeSingleClickOption() {
        IPreferenceStore store = WorkbenchPlugin.getDefault()
                .getPreferenceStore();
        boolean openOnSingleClick = store
                .getBoolean(IPreferenceConstants.OPEN_ON_SINGLE_CLICK);
        boolean selectOnHover = store
                .getBoolean(IPreferenceConstants.SELECT_ON_HOVER);
        boolean openAfterDelay = store
                .getBoolean(IPreferenceConstants.OPEN_AFTER_DELAY);
        int singleClickMethod = openOnSingleClick ? OpenStrategy.SINGLE_CLICK
                : OpenStrategy.DOUBLE_CLICK;
        if (openOnSingleClick) {
            if (selectOnHover) {
                singleClickMethod |= OpenStrategy.SELECT_ON_HOVER;
            }
            if (openAfterDelay) {
                singleClickMethod |= OpenStrategy.ARROW_KEYS_OPEN;
            }
        }
        OpenStrategy.setOpenMethod(singleClickMethod);
    }

    /*
     * Initializes the workbench fonts with the stored values.
     */
    private void initializeFonts() {
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                FontDefinition[] fontDefinitions = WorkbenchPlugin.getDefault()
                        .getThemeRegistry().getFonts();

                ThemeElementHelper.populateRegistry(getThemeManager()
                        .getCurrentTheme(), fontDefinitions, PrefUtil
                        .getInternalPreferenceStore());
            }
        });
    }

    /*
     * Initialize the workbench images.
     *
     * @param windowImages An array of the descriptors of the images to be used
     * in the corner of each window, or <code>null</code> if none. It is
     * expected that the array will contain the same icon, rendered at different
     * sizes.
     *
     * @since 3.0
     */
    private static void initializeImages() {
        ImageDescriptor[] windowImages = WorkbenchPlugin.getDefault()
                .getWindowImages();
        if (windowImages == null) {
            return;
        }

        Image[] images = new Image[windowImages.length];
        for (int i = 0; i < windowImages.length; ++i) {
            images[i] = windowImages[i].createImage();
        }
        Window.setDefaultImages(images);
    }

    /*
     * Take the workbenches' images out of the shared registry.
     *
     * @since 3.0
     */
    private void uninitializeImages() {
        WorkbenchImages.dispose();
        Image[] images = Window.getDefaultImages();
        Window.setDefaultImage(null);
        for (int i = 0; i < images.length; i++) {
            images[i].dispose();
        }
    }

    /*
     * Initialize the workbench colors.
     *
     * @since 3.0
     */
    private void initializeColors() {
        StartupThreading.runWithoutExceptions(new StartupRunnable() {
            public void runWithException() {
                WorkbenchColors.startup();
            }});
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public boolean isClosing() {
        return isClosing;
    }

    /**
     * Initializes all of the default services for the workbench. For
     * initializing the command-based services, this also parses the registry
     * and hooks up all the required listeners.
     */
    private final void initializeDefaultServices() {
        
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                serviceLocator.registerService(IWorkbench.class,
                        Workbench.this);
            }
        });
        
        // TODO Correctly order service initialization
 // there needs to be some serious consideration given to
 // the services, and hooking them up in the correct order
 final EvaluationService evaluationService = new EvaluationService();
        
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                serviceLocator.registerService(IEvaluationService.class,
                        evaluationService);
            }
        });
        
        

        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                serviceLocator.registerService(ISaveablesLifecycleListener.class,
                        new SaveablesList());
            }});
        
        /*
         * Phase 1 of the initialization of commands. When this phase completes,
         * all the services and managers will exist, and be accessible via the
         * getService(Object) method.
         */
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                Command.DEBUG_COMMAND_EXECUTION = Policy.DEBUG_COMMANDS;
                commandManager = new CommandManager();
            }});
        
        final CommandService [] commandService = new CommandService[1];
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                commandService[0] = new CommandService(commandManager);
                commandService[0].readRegistry();
                serviceLocator.registerService(ICommandService.class, commandService[0]);

            }});
        
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                ContextManager.DEBUG = Policy.DEBUG_CONTEXTS;
                contextManager = new ContextManager();
                }});
        
        final IContextService contextService = new ContextService(
                contextManager);
        
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                contextService.readRegistry();
                }});
        
        serviceLocator.registerService(IContextService.class, contextService);
    
        
        final IHandlerService [] handlerService = new IHandlerService[1];
    
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                handlerService[0] = new HandlerService(
                        commandService[0], evaluationService);
                handlerService[0].readRegistry();
            }});
        
        serviceLocator.registerService(IHandlerService.class, handlerService[0]);

        final IBindingService [] bindingService = new BindingService[1];
        
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                BindingManager.DEBUG = Policy.DEBUG_KEY_BINDINGS;
                bindingManager = new BindingManager(contextManager, commandManager);
                bindingService[0] = new BindingService(
                        bindingManager, commandService[0], Workbench.this);
                
            }});
        
        bindingService[0].readRegistryAndPreferences(commandService[0]);
        serviceLocator.registerService(IBindingService.class, bindingService[0]);

        final CommandImageManager commandImageManager = new CommandImageManager();
        final CommandImageService commandImageService = new CommandImageService(
                commandImageManager, commandService[0]);
        commandImageService.readRegistry();
        serviceLocator.registerService(ICommandImageService.class,
                commandImageService);
        
        final WorkbenchMenuService menuService = new WorkbenchMenuService(serviceLocator);
        
        serviceLocator.registerService(IMenuService.class, menuService);
        // the service must be registered before it is initialized - its
 // initialization uses the service locator to address a dependency on
 // the menu service
 StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                menuService.readRegistry();
            }});

        /*
         * Phase 2 of the initialization of commands. The source providers that
         * the workbench provides are creating and registered with the above
         * services. These source providers notify the services when particular
         * pieces of workbench state change.
         */
        final ISourceProviderService sourceProviderService = new SourceProviderService();
        serviceLocator.registerService(ISourceProviderService.class,
                sourceProviderService);
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                final ActiveShellSourceProvider activeShellSourceProvider = new ActiveShellSourceProvider(
                        Workbench.this);
                evaluationService.addSourceProvider(activeShellSourceProvider);
                handlerService[0].addSourceProvider(activeShellSourceProvider);
                contextService.addSourceProvider(activeShellSourceProvider);
                menuService.addSourceProvider(activeShellSourceProvider);
                sourceProviderService.registerProvider(activeShellSourceProvider);
            }});
        
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                final ActivePartSourceProvider activePartSourceProvider = new ActivePartSourceProvider(
                        Workbench.this);
                evaluationService.addSourceProvider(activePartSourceProvider);
                handlerService[0].addSourceProvider(activePartSourceProvider);
                contextService.addSourceProvider(activePartSourceProvider);
                menuService.addSourceProvider(activePartSourceProvider);
                sourceProviderService.registerProvider(activePartSourceProvider);
            }});
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                final ActiveContextSourceProvider activeContextSourceProvider = new ActiveContextSourceProvider(
                        contextService);
                evaluationService.addSourceProvider(activeContextSourceProvider);
                handlerService[0].addSourceProvider(activeContextSourceProvider);
                menuService.addSourceProvider(activeContextSourceProvider);
                sourceProviderService.registerProvider(activeContextSourceProvider);
            }});
        StartupThreading.runWithoutExceptions(new StartupRunnable() {

            public void runWithException() {
                final CurrentSelectionSourceProvider currentSelectionSourceProvider = new CurrentSelectionSourceProvider(
                        Workbench.this);
                evaluationService.addSourceProvider(currentSelectionSourceProvider);
                handlerService[0].addSourceProvider(currentSelectionSourceProvider);
                contextService.addSourceProvider(currentSelectionSourceProvider);
                menuService.addSourceProvider(currentSelectionSourceProvider);
                sourceProviderService.registerProvider(currentSelectionSourceProvider);
                
                actionSetSourceProvider = new ActionSetSourceProvider();
                evaluationService.addSourceProvider(actionSetSourceProvider);
                handlerService[0].addSourceProvider(actionSetSourceProvider);
                contextService.addSourceProvider(actionSetSourceProvider);
                menuService.addSourceProvider(actionSetSourceProvider);
                sourceProviderService.registerProvider(actionSetSourceProvider);
                
                FocusControlSourceProvider focusControl = new FocusControlSourceProvider();
                serviceLocator.registerService(IFocusService.class, focusControl);
                evaluationService.addSourceProvider(focusControl);
                handlerService[0].addSourceProvider(focusControl);
                contextService.addSourceProvider(focusControl);
                menuService.addSourceProvider(focusControl);
                sourceProviderService.registerProvider(focusControl);
                
                
                menuSourceProvider = new MenuSourceProvider();
                evaluationService.addSourceProvider(menuSourceProvider);
                handlerService[0].addSourceProvider(menuSourceProvider);
                contextService.addSourceProvider(menuSourceProvider);
                menuService.addSourceProvider(menuSourceProvider);
                sourceProviderService.registerProvider(menuSourceProvider);
            }});
        
        /*
         * Phase 3 of the initialization of commands. This handles the creation
         * of wrappers for legacy APIs. By the time this phase completes, any
         * code trying to access commands through legacy APIs should work.
         */
        workbenchContextSupport = new WorkbenchContextSupport(this,
                contextManager);
        workbenchCommandSupport = new WorkbenchCommandSupport(bindingManager,
                commandManager, contextManager, handlerService[0]);
        initializeCommandResolver();

        addWindowListener(windowListener);
        bindingManager.addBindingManagerListener(bindingManagerListener);

        serviceLocator.registerService(ISelectionConversionService.class,
                new SelectionConversionService());
    }

    /**
     * Returns true if the Workbench is in the process of starting.
     *
     * @return <code>true</code> if the Workbench is starting, but not yet
     * running the event loop.
     */
    public boolean isStarting() {
        return isStarting;
    }

    /*
     * Creates a new workbench window.
     *
     * @return the new workbench window
     */
    private WorkbenchWindow newWorkbenchWindow() {
        return new WorkbenchWindow(getNewWindowNumber());
    }

    /*
     * If a perspective was specified on the command line (-perspective) then
     * force that perspective to open in the active window.
     */
    private void forceOpenPerspective() {
        if (getWorkbenchWindowCount() == 0) {
            // there should be an open window by now, bail out.
 return;
        }

        String perspId = null;
        String [] commandLineArgs = Platform.getCommandLineArgs();
        for (int i = 0; i < commandLineArgs.length - 1; i++) {
            if (commandLineArgs[i].equalsIgnoreCase("-perspective")) { //$NON-NLS-1$
 perspId = commandLineArgs[i + 1];
                break;
            }
        }
        if (perspId == null) {
            return;
        }
        IPerspectiveDescriptor desc = getPerspectiveRegistry()
                .findPerspectiveWithId(perspId);
        if (desc == null) {
            return;
        }

        IWorkbenchWindow win = getActiveWorkbenchWindow();
        if (win == null) {
            win = getWorkbenchWindows()[0];
        }
        
        final String threadPerspId = perspId;
        final IWorkbenchWindow threadWin = win;
        StartupThreading.runWithoutExceptions(new StartupRunnable() {
            public void runWithException() throws Throwable {
                try {
                    showPerspective(threadPerspId, threadWin);
                } catch (WorkbenchException e) {
                    String msg = "Workbench exception showing specified command line perspective on startup."; //$NON-NLS-1$
 WorkbenchPlugin.log(msg, new Status(IStatus.ERROR,
                            PlatformUI.PLUGIN_ID, 0, msg, e));
                }
            }});
    }

    /**
     * Opens the initial workbench window.
     */
    /* package */void openFirstTimeWindow() {
        final boolean showProgress = PrefUtil.getAPIPreferenceStore()
                .getBoolean(
                        IWorkbenchPreferenceConstants.SHOW_PROGRESS_ON_STARTUP);

        if (!showProgress) {
            doOpenFirstTimeWindow();
        } else {
            // We don't know how many plug-ins will be loaded,
 // assume we are loading a tenth of the installed plug-ins.
 // (The Eclipse SDK loads 7 of 86 plug-ins at startup as of
 // 2005-5-20)
 final int expectedProgressCount = Math.max(1, WorkbenchPlugin
                    .getDefault().getBundleCount() / 10);

            runStartupWithProgress(expectedProgressCount, new Runnable () {
                public void run() {
                    doOpenFirstTimeWindow();
                }
            });
        }
    }

    private void runStartupWithProgress(final int expectedProgressCount,
            final Runnable runnable) {
        progressCount = 0;
        final double cutoff = 0.95;

        AbstractSplashHandler handler = getSplash();
        IProgressMonitor progressMonitor = null;
        if (handler != null)
            progressMonitor = handler.getBundleProgressMonitor();
         
        if (progressMonitor == null) {
            // cannot report progress (e.g. if the splash screen is not showing)
 // fall back to starting without showing progress.
 runnable.run();
        } else {
            progressMonitor.beginTask("", expectedProgressCount); //$NON-NLS-1$
 SynchronousBundleListener bundleListener = new StartupProgressBundleListener(
                    progressMonitor, (int) (expectedProgressCount * cutoff));
            WorkbenchPlugin.getDefault().addBundleListener(bundleListener);
            try {
                runnable.run();
                progressMonitor.subTask(WorkbenchMessages.Startup_Done);
                int remainingWork = expectedProgressCount
                        - Math.min(progressCount,
                                (int) (expectedProgressCount * cutoff));
                progressMonitor.worked(remainingWork);
                progressMonitor.done();
            } finally {
                WorkbenchPlugin.getDefault().removeBundleListener(
                        bundleListener);
            }
        }
    }

    private void doOpenFirstTimeWindow() {
        try {
            final IAdaptable input [] = new IAdaptable[1];
            StartupThreading.runWithoutExceptions(new StartupRunnable() {

                public void runWithException() throws Throwable {
                    input[0] = getDefaultPageInput();
                }});
            
            busyOpenWorkbenchWindow(getPerspectiveRegistry()
                    .getDefaultPerspective(), input[0]);
        } catch (final WorkbenchException e) {
            // Don't use the window's shell as the dialog parent,
 // as the window is not open yet (bug 76724).
 StartupThreading.runWithoutExceptions(new StartupRunnable() {

                public void runWithException() throws Throwable {
                    ErrorDialog.openError(null,
                            WorkbenchMessages.Problems_Opening_Page, e.getMessage(), e
                                    .getStatus());
                }});
        }
    }

    /*
     * Restores the workbench UI from the workbench state file (workbench.xml).
     *
     * @return a status object indicating OK if a window was opened,
     * RESTORE_CODE_RESET if no window was opened but one should be, and
     * RESTORE_CODE_EXIT if the workbench should close immediately
     */
    /* package */IStatus restoreState() {

        if (!getWorkbenchConfigurer().getSaveAndRestore()) {
            String msg = WorkbenchMessages.Workbench_restoreDisabled;
            return new Status(IStatus.WARNING, WorkbenchPlugin.PI_WORKBENCH,
                    IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, null);
        }
        // Read the workbench state file.
 final File stateFile = getWorkbenchStateFile();
        // If there is no state file cause one to open.
 if (stateFile == null || !stateFile.exists()) {
            String msg = WorkbenchMessages.Workbench_noStateToRestore;
            return new Status(IStatus.WARNING, WorkbenchPlugin.PI_WORKBENCH,
                    IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, null);
        }

        final IStatus result[] = { new Status(IStatus.OK,
                WorkbenchPlugin.PI_WORKBENCH, IStatus.OK, "", null) }; //$NON-NLS-1$
 SafeRunner.run(new SafeRunnable(WorkbenchMessages.ErrorReadingState) {
            public void run() throws Exception {
                FileInputStream input = new FileInputStream (stateFile);
                BufferedReader reader = new BufferedReader (
                        new InputStreamReader (input, "utf-8")); //$NON-NLS-1$
 IMemento memento = XMLMemento.createReadRoot(reader);

                // Validate known version format
 String version = memento
                        .getString(IWorkbenchConstants.TAG_VERSION);
                boolean valid = false;
                for (int i = 0; i < VERSION_STRING.length; i++) {
                    if (VERSION_STRING[i].equals(version)) {
                        valid = true;
                        break;
                    }
                }
                if (!valid) {
                    reader.close();
                    String msg = WorkbenchMessages.Invalid_workbench_state_ve;
                    MessageDialog.openError((Shell) null,
                            WorkbenchMessages.Restoring_Problems, msg);
                    stateFile.delete();
                    result[0] = new Status(IStatus.ERROR,
                            WorkbenchPlugin.PI_WORKBENCH,
                            IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, null);
                    return;
                }

                // Validate compatible version format
 // We no longer support the release 1.0 format
 if (VERSION_STRING[0].equals(version)) {
                    reader.close();
                    String msg = WorkbenchMessages.Workbench_incompatibleSavedStateVersion;
                    boolean ignoreSavedState = new MessageDialog(null,
                            WorkbenchMessages.Workbench_incompatibleUIState,
                            null, msg, MessageDialog.WARNING, new String [] {
                                    IDialogConstants.OK_LABEL,
                                    IDialogConstants.CANCEL_LABEL }, 0).open() == 0;
                    // OK is the default
 if (ignoreSavedState) {
                        stateFile.delete();
                        result[0] = new Status(IStatus.WARNING,
                                WorkbenchPlugin.PI_WORKBENCH,
                                IWorkbenchConfigurer.RESTORE_CODE_RESET, msg,
                                null);
                    } else {
                        result[0] = new Status(IStatus.WARNING,
                                WorkbenchPlugin.PI_WORKBENCH,
                                IWorkbenchConfigurer.RESTORE_CODE_EXIT, msg,
                                null);
                    }
                    return;
                }

                // Restore the saved state
 final IStatus restoreResult = restoreState(memento);
                reader.close();
                if (restoreResult.getSeverity() == IStatus.ERROR) {
                    StartupThreading
                            .runWithoutExceptions(new StartupRunnable() {

                                public void runWithException() throws Throwable {
                                    ErrorDialog
                                            .openError(
                                                    null,
                                                    WorkbenchMessages.Workspace_problemsTitle,
                                                    WorkbenchMessages.Workbench_problemsRestoringMsg,
                                                    restoreResult);
                                }
                            });

                }
            }

            public void handleException(final Throwable e) {
                StartupThreading.runWithoutExceptions(new StartupRunnable() {

                    public void runWithException() {
                        handle(e);
                        String msg = e.getMessage() == null ? "" : e.getMessage(); //$NON-NLS-1$
 result[0] = new Status(IStatus.ERROR,
                                WorkbenchPlugin.PI_WORKBENCH,
                                IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, e);
                        stateFile.delete();
                    }});
            }
            
            private void handle(final Throwable e) {
                super.handleException(e);
            }
        });
        // ensure at least one window was opened
 if (result[0].isOK() && windowManager.getWindows().length == 0) {
            String msg = WorkbenchMessages.Workbench_noWindowsRestored;
            result[0] = new Status(IStatus.ERROR, WorkbenchPlugin.PI_WORKBENCH,
                    IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, null);
        }
        return result[0];
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public IWorkbenchWindow openWorkbenchWindow(IAdaptable input)
            throws WorkbenchException {
        return openWorkbenchWindow(getPerspectiveRegistry()
                .getDefaultPerspective(), input);
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public IWorkbenchWindow openWorkbenchWindow(final String perspID,
            final IAdaptable input) throws WorkbenchException {
        // Run op in busy cursor.
 final Object [] result = new Object [1];
        BusyIndicator.showWhile(null, new Runnable () {
            public void run() {
                try {
                    result[0] = busyOpenWorkbenchWindow(perspID, input);
                } catch (WorkbenchException e) {
                    result[0] = e;
                }
            }
        });
        if (result[0] instanceof IWorkbenchWindow) {
            return (IWorkbenchWindow) result[0];
        } else if (result[0] instanceof WorkbenchException) {
            throw (WorkbenchException) result[0];
        } else {
            throw new WorkbenchException(
                    WorkbenchMessages.Abnormal_Workbench_Conditi);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.IWorkbench#restoreWorkbenchWindow(org.eclipse.ui.IMemento)
     */
    IWorkbenchWindow restoreWorkbenchWindow(IMemento memento)
            throws WorkbenchException {
        WorkbenchWindow newWindow = newWorkbenchWindow();
        newWindow.create();

        windowManager.add(newWindow);

        // whether the window was opened
 boolean opened = false;

        try {
            newWindow.restoreState(memento, null);
            newWindow.fireWindowRestored();
            newWindow.open();
            opened = true;
        } finally {
            if (!opened) {
                newWindow.close();
            }
        }

        return newWindow;
    }

    /*
     * Record the workbench UI in a document
     */
    private XMLMemento recordWorkbenchState() {
        XMLMemento memento = XMLMemento
                .createWriteRoot(IWorkbenchConstants.TAG_WORKBENCH);
        final IStatus status = saveState(memento);
        if (status.getSeverity() != IStatus.OK) {
            // don't use newWindow as parent because it has not yet been opened
 // (bug 76724)
 StartupThreading.runWithoutExceptions(new StartupRunnable() {

                public void runWithException() throws Throwable {
                    ErrorDialog.openError(null,
                            WorkbenchMessages.Workbench_problemsSaving,
                            WorkbenchMessages.Workbench_problemsSavingMsg, status);
                }});
            
        }
        return memento;
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public boolean restart() {
        // this is the return code from run() to trigger a restart
 return close(PlatformUI.RETURN_RESTART, false);
    }

    /*
     * Restores the state of the previously saved workbench
     */
    private IStatus restoreState(final IMemento memento) {

        final MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID,
                IStatus.OK, WorkbenchMessages.Workbench_problemsRestoring, null);

        final boolean showProgress = PrefUtil.getAPIPreferenceStore()
                .getBoolean(
                        IWorkbenchPreferenceConstants.SHOW_PROGRESS_ON_STARTUP);

        try {
            /*
             * Restored windows will be set in the createdWindows field to be
             * used by the openWindowsAfterRestore() method
             */
            if (!showProgress) {
                doRestoreState(memento, result);
            } else {
                // Retrieve how many plug-ins were loaded while restoring the
 // workbench
 Integer lastProgressCount = memento
                        .getInteger(IWorkbenchConstants.TAG_PROGRESS_COUNT);

                // If we don't know how many plug-ins were loaded last time,
 // assume we are loading half of the installed plug-ins.
 final int expectedProgressCount = Math.max(1,
                        lastProgressCount == null ? WorkbenchPlugin
                                .getDefault().getBundleCount() / 2
                                : lastProgressCount.intValue());

                runStartupWithProgress(expectedProgressCount, new Runnable () {
                    public void run() {
                        doRestoreState(memento, result);
                    }
                });
            }
        } finally {
            openWindowsAfterRestore();
        }
        return result;
    }

    /**
     * Returns the ids of all plug-ins that extend the
     * <code>org.eclipse.ui.startup</code> extension point.
     *
     * @return the ids of all plug-ins containing 1 or more startup extensions
     */
    public String [] getEarlyActivatedPlugins() {
        IExtensionPoint point = Platform.getExtensionRegistry()
                .getExtensionPoint(PlatformUI.PLUGIN_ID,
                        IWorkbenchRegistryConstants.PL_STARTUP);
        IExtension[] extensions = point.getExtensions();
        ArrayList pluginIds = new ArrayList (extensions.length);
        for (int i = 0; i < extensions.length; i++) {
            String id = extensions[i].getNamespace();
            if (!pluginIds.contains(id)) {
                pluginIds.add(id);
            }
        }
        return (String []) pluginIds.toArray(new String [pluginIds.size()]);
    }

    /**
     * Returns the ids of the early activated plug-ins that have been disabled
     * by the user.
     *
     * @return the ids of the early activated plug-ins that have been disabled
     * by the user
     */
    public String [] getDisabledEarlyActivatedPlugins() {
        String pref = PrefUtil.getInternalPreferenceStore().getString(
                IPreferenceConstants.PLUGINS_NOT_ACTIVATED_ON_STARTUP);
        return Util.getArrayFromList(pref, ";"); //$NON-NLS-1$
 }

    /*
     * Starts all plugins that extend the <code> org.eclipse.ui.startup </code>
     * extension point, and that the user has not disabled via the preference
     * page.
     */
    private void startPlugins() {
        IExtensionRegistry registry = Platform.getExtensionRegistry();

        // bug 55901: don't use getConfigElements directly, for pre-3.0
 // compat, make sure to allow both missing class
 // attribute and a missing startup element
 IExtensionPoint point = registry.getExtensionPoint(
                PlatformUI.PLUGIN_ID, IWorkbenchRegistryConstants.PL_STARTUP);

        final IExtension[] extensions = point.getExtensions();
        if (extensions.length == 0) {
            return;
        }
        Job job = new Job("Workbench early startup") { //$NON-NLS-1$
 protected IStatus run(IProgressMonitor monitor) {
                HashSet disabledPlugins = new HashSet (Arrays
                        .asList(getDisabledEarlyActivatedPlugins()));
                monitor.beginTask(WorkbenchMessages.Workbench_startingPlugins,
                        extensions.length);
                for (int i = 0; i < extensions.length; ++i) {
                    if (monitor.isCanceled() || !isRunning()) {
                        return Status.CANCEL_STATUS;
                    }
                    IExtension extension = extensions[i];

                    // if the plugin is not in the set of disabled plugins, then
 // execute the code to start it
 if (!disabledPlugins.contains(extension.getNamespace())) {
                        monitor.subTask(extension.getNamespace());
                        SafeRunner.run(new EarlyStartupRunnable(extension));
                    }
                    monitor.worked(1);
                }
                monitor.done();
                return Status.OK_STATUS;
            }

            public boolean belongsTo(Object family) {
                return EARLY_STARTUP_FAMILY.equals(family);
            }
        };
        job.setSystem(true);
        job.schedule();
    }

    /**
     * Internal method for running the workbench UI. This entails processing and
     * dispatching events until the workbench is closed or restarted.
     *
     * @return return code {@link PlatformUI#RETURN_OK RETURN_OK}for normal
     * exit; {@link PlatformUI#RETURN_RESTART RETURN_RESTART}if the
     * workbench was terminated with a call to
     * {@link IWorkbench#restart IWorkbench.restart};
     * {@link PlatformUI#RETURN_UNSTARTABLE RETURN_UNSTARTABLE}if the
     * workbench could not be started; other values reserved for future
     * use
     * @since 3.0
     */
    private int runUI() {
        UIStats.start(UIStats.START_WORKBENCH, "Workbench"); //$NON-NLS-1$

        // deadlock code
 boolean avoidDeadlock = true;

        String [] commandLineArgs = Platform.getCommandLineArgs();
        for (int i = 0; i < commandLineArgs.length; i++) {
            if (commandLineArgs[i].equalsIgnoreCase("-allowDeadlock")) { //$NON-NLS-1$
 avoidDeadlock = false;
            }
        }

        final UISynchronizer synchronizer;
        
        if (avoidDeadlock) {
            UILockListener uiLockListener = new UILockListener(display);
            Platform.getJobManager().setLockListener(uiLockListener);
            synchronizer = new UISynchronizer(display, uiLockListener);
            display
                    .setSynchronizer(synchronizer);
            // declare the main thread to be a startup thread.
 UISynchronizer.startupThread.set(Boolean.TRUE);
        }
        else
            synchronizer = null;
        
        // prime the splash nice and early
 if (createSplash)
            createSplashWrapper();

        // ModalContext should not spin the event loop (there is no UI yet to
 // block)
 ModalContext.setAllowReadAndDispatch(false);

        // if the -debug command line argument is used and the event loop is
 // being
 // run while starting the Workbench, log a warning.
 if (WorkbenchPlugin.getDefault().isDebugging()) {
            display.asyncExec(new Runnable () {
                public void run() {
                    if (isStarting()) {
                        WorkbenchPlugin
                                .log(StatusUtil
                                        .newStatus(
                                                IStatus.WARNING,
                                                "Event loop should not be run while the Workbench is starting.", //$NON-NLS-1$
 new RuntimeException ()));
                    }
                }
            });
        }

        Listener closeListener = new Listener() {
            public void handleEvent(Event event) {
                event.doit = close();
            }
        };

        // Initialize an exception handler.
 Window.IExceptionHandler handler = ExceptionHandler.getInstance();

        try {
            // react to display close event by closing workbench nicely
 display.addListener(SWT.Close, closeListener);

            // install backstop to catch exceptions thrown out of event loop
 Window.setExceptionHandler(handler);

            final boolean [] initOK = new boolean[1];
            
            if (getSplash() != null) {
                
                final boolean[] initDone = new boolean[]{false};
                Thread initThread = new Thread () {
                /* (non-Javadoc)
                 * @see java.lang.Thread#run()
                 */
                public void run() {
                    try {
                        //declare us to be a startup thread so that our syncs will be executed
 UISynchronizer.startupThread.set(Boolean.TRUE);
                        initOK[0] = Workbench.this.init();
                    } finally {
                        initDone[0] = true;
                        display.wake();
                    }
                }};
                initThread.start();
                while (true) {
                    if (!display.readAndDispatch()) {
                        if (initDone[0])
                            break;
                        display.sleep();
                    }
                    
                }
            }
            else {
                // initialize workbench and restore or open one window
 initOK[0] = init();

            }
            // drop the splash screen now that a workbench window is up
 Platform.endSplash();

            // let the advisor run its start up code
 if (initOK[0]) {
                advisor.postStartup(); // may trigger a close/restart
 }

            if (initOK[0] && runEventLoop) {
                // start eager plug-ins
 startPlugins();
                addStartupRegistryListener();

                // WWinPluginAction.refreshActionList();

                display.asyncExec(new Runnable () {
                    public void run() {
                        UIStats.end(UIStats.START_WORKBENCH, this, "Workbench"); //$NON-NLS-1$
 UIStats.startupComplete();
                    }
                });

                getWorkbenchTestable().init(display, this);

                // allow ModalContext to spin the event loop
 ModalContext.setAllowReadAndDispatch(true);
                isStarting = false;

                if (synchronizer != null)
                    synchronizer.started();
                // the event loop
 runEventLoop(handler, display);
            }

        } catch (final Exception e) {
            if (!display.isDisposed()) {
                handler.handleException(e);
            } else {
                String msg = "Exception in Workbench.runUI after display was disposed"; //$NON-NLS-1$
 WorkbenchPlugin.log(msg, new Status(IStatus.ERROR,
                        WorkbenchPlugin.PI_WORKBENCH, 1, msg, e));
            }
        } finally {
            // mandatory clean up

            // The runEventLoop flag may not have been cleared if an exception
 // occurred
 // Needs to be false to ensure PlatformUI.isWorkbenchRunning()
 // returns false.
 runEventLoop = false;

            if (!display.isDisposed()) {
                display.removeListener(SWT.Close, closeListener);
            }
        }

        // restart or exit based on returnCode
 return returnCode;
    }

    /*
     * Runs an event loop for the workbench.
     */
    private void runEventLoop(Window.IExceptionHandler handler, Display display) {
        runEventLoop = true;
        while (runEventLoop) {
            try {
                if (!display.readAndDispatch()) {
                    getAdvisor().eventLoopIdle(display);
                }
            } catch (Throwable t) {
                handler.handleException(t);
            }
        }
    }

    /*
     * Saves the current state of the workbench so it can be restored later on
     */
    private IStatus saveState(IMemento memento) {
        MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK,
                WorkbenchMessages.Workbench_problemsSaving, null);

        // Save the version number.
 memento.putString(IWorkbenchConstants.TAG_VERSION, VERSION_STRING[1]);

        // Save how many plug-ins were loaded while restoring the workbench
 if (progressCount != -1) {
            memento.putInteger(IWorkbenchConstants.TAG_PROGRESS_COUNT,
                    progressCount);
        }

        // Save the advisor state.
 IMemento advisorState = memento
                .createChild(IWorkbenchConstants.TAG_WORKBENCH_ADVISOR);
        result.add(getAdvisor().saveState(advisorState));

        // Save the workbench windows.
 IWorkbenchWindow[] windows = getWorkbenchWindows();
        for (int nX = 0; nX < windows.length; nX++) {
            WorkbenchWindow window = (WorkbenchWindow) windows[nX];
            IMemento childMem = memento
                    .createChild(IWorkbenchConstants.TAG_WINDOW);
            result.merge(window.saveState(childMem));
        }
        result.add(getEditorHistory().saveState(
                memento.createChild(IWorkbenchConstants.TAG_MRU_LIST)));
        return result;
    }

    /*
     * Save the workbench UI in a persistence file.
     */
    private boolean saveMementoToFile(XMLMemento memento) {
        // Save it to a file.
 // XXX: nobody currently checks the return value of this method.
 File stateFile = getWorkbenchStateFile();
        if (stateFile == null) {
            return false;
        }
        try {
            FileOutputStream stream = new FileOutputStream (stateFile);
            OutputStreamWriter writer = new OutputStreamWriter (stream, "utf-8"); //$NON-NLS-1$
 memento.save(writer);
            writer.close();
        } catch (IOException e) {
            stateFile.delete();
            MessageDialog.openError((Shell) null,
                    WorkbenchMessages.SavingProblem,
                    WorkbenchMessages.ProblemSavingState);
            return false;
        }

        // Success !
 return true;
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public IWorkbenchPage showPerspective(String perspectiveId,
            IWorkbenchWindow window) throws WorkbenchException {
        Assert.isNotNull(perspectiveId);

        // If the specified window has the requested perspective open, then the
 // window
 // is given focus and the perspective is shown. The page's input is
 // ignored.
 WorkbenchWindow win = (WorkbenchWindow) window;
        if (win != null) {
            WorkbenchPage page = win.getActiveWorkbenchPage();
            if (page != null) {
                IPerspectiveDescriptor perspectives[] = page
                        .getOpenPerspectives();
                for (int i = 0; i < perspectives.length; i++) {
                    IPerspectiveDescriptor persp = perspectives[i];
                    if (perspectiveId.equals(persp.getId())) {
                        win.makeVisible();
                        page.setPerspective(persp);
                        return page;
                    }
                }
            }
        }

        // If another window that has the workspace root as input and the
 // requested
 // perpective open and active, then the window is given focus.
 IAdaptable input = getDefaultPageInput();
        IWorkbenchWindow[] windows = getWorkbenchWindows();
        for (int i = 0; i < windows.length; i++) {
            win = (WorkbenchWindow) windows[i];
            if (window != win) {
                WorkbenchPage page = win.getActiveWorkbenchPage();
                if (page != null) {
                    boolean inputSame = false;
                    if (input == null) {
                        inputSame = (page.getInput() == null);
                    } else {
                        inputSame = input.equals(page.getInput());
                    }
                    if (inputSame) {
                        Perspective persp = page.getActivePerspective();
                        if (persp != null) {
                            IPerspectiveDescriptor desc = persp.getDesc();
                            if (desc != null) {
                                if (perspectiveId.equals(desc.getId())) {
                                    Shell shell = win.getShell();
                                    shell.open();
                                    if (shell.getMinimized()) {
                                        shell.setMinimized(false);
                                    }
                                    return page;
                                }
                            }
                        }
                    }
                }
            }
        }

        // Otherwise the requested perspective is opened and shown in the
 // specified
 // window or in a new window depending on the current user preference
 // for opening
 // perspectives, and that window is given focus.
 win = (WorkbenchWindow) window;
        if (win != null) {
            IPreferenceStore store = WorkbenchPlugin.getDefault()
                    .getPreferenceStore();
            int mode = store.getInt(IPreferenceConstants.OPEN_PERSP_MODE);
            IWorkbenchPage page = win.getActiveWorkbenchPage();
            IPerspectiveDescriptor persp = null;
            if (page != null) {
                persp = page.getPerspective();
            }

            // Only open a new window if user preference is set and the window
 // has an active perspective.
 if (IPreferenceConstants.OPM_NEW_WINDOW == mode && persp != null) {
                IWorkbenchWindow newWindow = openWorkbenchWindow(perspectiveId,
                        input);
                return newWindow.getActivePage();
            }

            IPerspectiveDescriptor desc = getPerspectiveRegistry()
                    .findPerspectiveWithId(perspectiveId);
            if (desc == null) {
                throw new WorkbenchException(
                        NLS
                                .bind(
                                        WorkbenchMessages.WorkbenchPage_ErrorCreatingPerspective,
                                        perspectiveId));
            }
            win.getShell().open();
            if (page == null) {
                page = win.openPage(perspectiveId, input);
            } else {
                page.setPerspective(desc);
            }
            return page;
        }

        // Just throw an exception....
 throw new WorkbenchException(NLS
                .bind(WorkbenchMessages.Workbench_showPerspectiveError,
                        perspectiveId));
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public IWorkbenchPage showPerspective(String perspectiveId,
            IWorkbenchWindow window, IAdaptable input)
            throws WorkbenchException {
        Assert.isNotNull(perspectiveId);

        // If the specified window has the requested perspective open and the
 // same requested
 // input, then the window is given focus and the perspective is shown.
 boolean inputSameAsWindow = false;
        WorkbenchWindow win = (WorkbenchWindow) window;
        if (win != null) {
            WorkbenchPage page = win.getActiveWorkbenchPage();
            if (page != null) {
                boolean inputSame = false;
                if (input == null) {
                    inputSame = (page.getInput() == null);
                } else {
                    inputSame = input.equals(page.getInput());
                }
                if (inputSame) {
                    inputSameAsWindow = true;
                    IPerspectiveDescriptor perspectives[] = page
                            .getOpenPerspectives();
                    for (int i = 0; i < perspectives.length; i++) {
                        IPerspectiveDescriptor persp = perspectives[i];
                        if (perspectiveId.equals(persp.getId())) {
                            win.makeVisible();
                            page.setPerspective(persp);
                            return page;
                        }
                    }
                }
            }
        }

        // If another window has the requested input and the requested
 // perpective open and active, then that window is given focus.
 IWorkbenchWindow[] windows = getWorkbenchWindows();
        for (int i = 0; i < windows.length; i++) {
            win = (WorkbenchWindow) windows[i];
            if (window != win) {
                WorkbenchPage page = win.getActiveWorkbenchPage();
                if (page != null) {
                    boolean inputSame = false;
                    if (input == null) {
                        inputSame = (page.getInput() == null);
                    } else {
                        inputSame = input.equals(page.getInput());
                    }
                    if (inputSame) {
                        Perspective persp = page.getActivePerspective();
                        if (persp != null) {
                            IPerspectiveDescriptor desc = persp.getDesc();
                            if (desc != null) {
                                if (perspectiveId.equals(desc.getId())) {
                                    win.getShell().open();
                                    return page;
                                }
                            }
                        }
                    }
                }
            }
        }

        // If the specified window has the same requested input but not the
 // requested
 // perspective, then the window is given focus and the perspective is
 // opened and shown
 // on condition that the user preference is not to open perspectives in
 // a new window.
 win = (WorkbenchWindow) window;
        if (inputSameAsWindow && win != null) {
            IPreferenceStore store = WorkbenchPlugin.getDefault()
                    .getPreferenceStore();
            int mode = store.getInt(IPreferenceConstants.OPEN_PERSP_MODE);

            if (IPreferenceConstants.OPM_NEW_WINDOW != mode) {
                IWorkbenchPage page = win.getActiveWorkbenchPage();
                IPerspectiveDescriptor desc = getPerspectiveRegistry()
                        .findPerspectiveWithId(perspectiveId);
                if (desc == null) {
                    throw new WorkbenchException(
                            NLS
                                    .bind(
                                            WorkbenchMessages.WorkbenchPage_ErrorCreatingPerspective,
                                            perspectiveId));
                }
                win.getShell().open();
                if (page == null) {
                    page = win.openPage(perspectiveId, input);
                } else {
                    page.setPerspective(desc);
                }
                return page;
            }
        }

        // If the specified window has no active perspective, then open the
 // requested perspective and show the specified window.
 if (win != null) {
            IWorkbenchPage page = win.getActiveWorkbenchPage();
            IPerspectiveDescriptor persp = null;
            if (page != null) {
                persp = page.getPerspective();
            }
            if (persp == null) {
                IPerspectiveDescriptor desc = getPerspectiveRegistry()
                        .findPerspectiveWithId(perspectiveId);
                if (desc == null) {
                    throw new WorkbenchException(
                            NLS
                                    .bind(
                                            WorkbenchMessages.WorkbenchPage_ErrorCreatingPerspective,
                                            perspectiveId));
                }
                win.getShell().open();
                if (page == null) {
                    page = win.openPage(perspectiveId, input);
                } else {
                    page.setPerspective(desc);
                }
                return page;
            }
        }

        // Otherwise the requested perspective is opened and shown in a new
 // window, and the
 // window is given focus.
 IWorkbenchWindow newWindow = openWorkbenchWindow(perspectiveId, input);
        return newWindow.getActivePage();
    }

    /*
     * Shuts down the application.
     */
    private void shutdown() {
        // shutdown application-specific portions first
 advisor.postShutdown();

        // notify regular workbench clients of shutdown, and clear the list when
 // done
 firePostShutdown();
        workbenchListeners.clear();

        cancelEarlyStartup();

        // for dynamic UI
 Platform.getExtensionRegistry().removeRegistryChangeListener(
                extensionEventHandler);
        Platform.getExtensionRegistry().removeRegistryChangeListener(
                startupRegistryListener);

        ((GrabFocus) Tweaklets.get(GrabFocus.KEY)).dispose();
        
        // Bring down all of the services.
 serviceLocator.dispose();

        workbenchActivitySupport.dispose();
        WorkbenchHelpSystem.disposeIfNecessary();

        // shutdown the rest of the workbench
 WorkbenchColors.shutdown();
        activityHelper.shutdown();
        uninitializeImages();
        if (WorkbenchPlugin.getDefault() != null) {
            WorkbenchPlugin.getDefault().reset();
        }
        WorkbenchThemeManager.getInstance().dispose();
        PropertyPageContributorManager.getManager().dispose();
        ObjectActionContributorManager.getManager().dispose();
        if (tracker != null) {
            tracker.close();
        }
    }

    /**
     * Cancels the early startup job, if it's still running.
     */
    private void cancelEarlyStartup() {
        Platform.getJobManager().cancel(EARLY_STARTUP_FAMILY);
        // We do not currently wait for any plug-in currently being started to
 // complete
 // (e.g. by doing a join on EARLY_STARTUP_FAMILY), since they may do a
 // syncExec,
 // which would hang. See bug 94537 for rationale.
 }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public IDecoratorManager getDecoratorManager() {
        return WorkbenchPlugin.getDefault().getDecoratorManager();
    }

    /*
     * Returns the workbench window which was last known being the active one,
     * or <code> null </code> .
     */
    private WorkbenchWindow getActivatedWindow() {
        if (activatedWindow != null) {
            Shell shell = activatedWindow.getShell();
            if (shell != null && !shell.isDisposed()) {
                return activatedWindow;
            }
        }

        return null;
    }

    /*
     * Sets the workbench window which was last known being the active one, or
     * <code> null </code> .
     */
    /* package */
    void setActivatedWindow(WorkbenchWindow window) {
        activatedWindow = window;
    }

    /**
     * Returns the unique object that applications use to configure the
     * workbench.
     * <p>
     * IMPORTANT This method is declared package-private to prevent regular
     * plug-ins from downcasting IWorkbench to Workbench and getting hold of the
     * workbench configurer that would allow them to tamper with the workbench.
     * The workbench configurer is available only to the application.
     * </p>
     */
    /* package */
    WorkbenchConfigurer getWorkbenchConfigurer() {
        if (workbenchConfigurer == null) {
            workbenchConfigurer = new WorkbenchConfigurer();
        }
        return workbenchConfigurer;
    }

    /**
     * Returns the workbench advisor that created this workbench.
     * <p>
     * IMPORTANT This method is declared package-private to prevent regular
     * plug-ins from downcasting IWorkbench to Workbench and getting hold of the
     * workbench advisor that would allow them to tamper with the workbench. The
     * workbench advisor is internal to the application.
     * </p>
     */
    /* package */
    WorkbenchAdvisor getAdvisor() {
        return advisor;
    }

    /*
     * (non-Javadoc) Method declared on IWorkbench.
     */
    public Display getDisplay() {
        return display;
    }

    /**
     * Returns the default perspective id, which may be <code>null</code>.
     *
     * @return the default perspective id, or <code>null</code>
     */
    public String getDefaultPerspectiveId() {
        return getAdvisor().getInitialWindowPerspectiveId();
    }

    /**
     * Returns the default workbench window page input.
     *
     * @return the default window page input or <code>null</code> if none
     */
    public IAdaptable getDefaultPageInput() {
        return getAdvisor().getDefaultPageInput();
    }

    /**
     * Returns the id of the preference page that should be presented most
     * prominently.
     *
     * @return the id of the preference page, or <code>null</code> if none
     */
    public String getMainPreferencePageId() {
        String id = getAdvisor().getMainPreferencePageId();
        return id;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.IWorkbench
     * @since 3.0
     */
    public IElementFactory getElementFactory(String factoryId) {
        Assert.isNotNull(factoryId);
        return WorkbenchPlugin.getDefault().getElementFactory(factoryId);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.IWorkbench#getProgressService()
     */
    public IProgressService getProgressService() {
        return ProgressManager.getInstance();
    }

    private WorkbenchActivitySupport workbenchActivitySupport;

    private WorkbenchCommandSupport workbenchCommandSupport;

    private WorkbenchContextSupport workbenchContextSupport;

    /**
     * The single instance of the binding manager used by the workbench. This is
     * initialized in <code>Workbench.init(Display)</code> and then never
     * changed. This value will only be <code>null</code> if the
     * initialization call has not yet completed.
     *
     * @since 3.1
     */
    private BindingManager bindingManager;

    /**
     * The single instance of the command manager used by the workbench. This is
     * initialized in <code>Workbench.init(Display)</code> and then never
     * changed. This value will only be <code>null</code> if the
     * initialization call has not yet completed.
     *
     * @since 3.1
     */
    private CommandManager commandManager;

    /**
     * The single instance of the context manager used by the workbench. This is
     * initialized in <code>Workbench.init(Display)</code> and then never
     * changed. This value will only be <code>null</code> if the
     * initialization call has not yet completed.
     *
     * @since 3.1
     */
    private ContextManager contextManager;

    public IWorkbenchActivitySupport getActivitySupport() {
        return workbenchActivitySupport;
    }

    public IWorkbenchCommandSupport getCommandSupport() {
        return workbenchCommandSupport;
    }

    public IWorkbenchContextSupport getContextSupport() {
        return workbenchContextSupport;
    }

    private final IWindowListener windowListener = new IWindowListener() {

        public void windowActivated(IWorkbenchWindow window) {
            updateActiveWorkbenchWindowMenuManager(true);
        }

        public void windowClosed(IWorkbenchWindow window) {
            updateActiveWorkbenchWindowMenuManager(true);
        }

        public void windowDeactivated(IWorkbenchWindow window) {
            updateActiveWorkbenchWindowMenuManager(true);
        }

        public void windowOpened(IWorkbenchWindow window) {
            updateActiveWorkbenchWindowMenuManager(true);
        }
    };

    private final IBindingManagerListener bindingManagerListener = new IBindingManagerListener() {

        public void bindingManagerChanged(
                BindingManagerEvent bindingManagerEvent) {
            if (bindingManagerEvent.isActiveBindingsChanged()) {
                updateActiveWorkbenchWindowMenuManager(true);
            }
        }
    };

    /**
     * The source provider that tracks the activation of action sets within the
     * workbench. This source provider is <code>null</code> until
     * {@link #initializeDefaultServices()} is called.
     */
    private ActionSetSourceProvider actionSetSourceProvider;

    private WorkbenchWindow activeWorkbenchWindow = null;

    private void updateActiveWorkbenchWindowMenuManager(boolean textOnly) {
        if (activeWorkbenchWindow != null) {
            activeWorkbenchWindow
                    .removeActionSetsListener(actionSetSourceProvider);
            activeWorkbenchWindow = null;
        }
        boolean actionSetsUpdated = false;

        final IWorkbenchWindow workbenchWindow = getActiveWorkbenchWindow();

        if (workbenchWindow instanceof WorkbenchWindow) {
            activeWorkbenchWindow = (WorkbenchWindow) workbenchWindow;
            if (activeWorkbenchWindow.isClosing()) {
                return;
            }

            // Update the action sets.
 final Shell windowShell = activeWorkbenchWindow.getShell();
            final Shell activeShell = getDisplay().getActiveShell();
            final IContextService service = (IContextService) getService(IContextService.class);
            if (Util.equals(windowShell, activeShell)
                    || service.getShellType(activeShell) == IContextService.TYPE_WINDOW) {
                activeWorkbenchWindow
                        .addActionSetsListener(actionSetSourceProvider);
                final WorkbenchPage page = activeWorkbenchWindow
                        .getActiveWorkbenchPage();
                final IActionSetDescriptor[] newActionSets;
                if (page != null) {
                    newActionSets = page.getActionSets();
                    final ActionSetsEvent event = new ActionSetsEvent(
                            newActionSets);
                    actionSetSourceProvider.actionSetsChanged(event);
                    actionSetsUpdated = true;
                }
            }

            final MenuManager menuManager = activeWorkbenchWindow
                    .getMenuManager();

            if (textOnly) {
                menuManager.update(IAction.TEXT);
            } else {
                menuManager.updateAll(true);
            }
        }

        if (!actionSetsUpdated) {
            final ActionSetsEvent event = new ActionSetsEvent(null);
            actionSetSourceProvider.actionSetsChanged(event);
        }
    }

    private ActivityPersistanceHelper activityHelper;

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.IWorkbench#getIntroManager()
     */
    public IIntroManager getIntroManager() {
        return getWorkbenchIntroManager();
    }

    /**
     * @return the workbench intro manager
     * @since 3.0
     */
    /* package */WorkbenchIntroManager getWorkbenchIntroManager() {
        if (introManager == null) {
            introManager = new WorkbenchIntroManager(this);
        }
        return introManager;
    }

    private WorkbenchIntroManager introManager;

    /**
     * @return the intro extension for this workbench.
     *
     * @since 3.0
     */
    public IntroDescriptor getIntroDescriptor() {
        return introDescriptor;
    }

    /**
     * This method exists as a test hook. This method should <strong>NEVER</strong>
     * be called by clients.
     *
     * @param descriptor
     * The intro descriptor to use.
     * @since 3.0
     */
    public void setIntroDescriptor(IntroDescriptor descriptor) {
        if (getIntroManager().getIntro() != null) {
            getIntroManager().closeIntro(getIntroManager().getIntro());
        }
        introDescriptor = descriptor;
    }

    /**
     * The descriptor for the intro extension that is valid for this workspace,
     * <code>null</code> if none.
     */
    private IntroDescriptor introDescriptor;

    private IExtensionTracker tracker;

    private IRegistryChangeListener startupRegistryListener = new IRegistryChangeListener() {

        /*
         * (non-Javadoc)
         *
         * @see org.eclipse.core.runtime.IRegistryChangeListener#registryChanged(org.eclipse.core.runtime.IRegistryChangeEvent)
         */
        public void registryChanged(IRegistryChangeEvent event) {
            final IExtensionDelta[] deltas = event.getExtensionDeltas(
                    PlatformUI.PLUGIN_ID,
                    IWorkbenchRegistryConstants.PL_STARTUP);
            if (deltas.length == 0) {
                return;
            }
            final String disabledPlugins = PrefUtil
                    .getInternalPreferenceStore()
                    .getString(
                            IPreferenceConstants.PLUGINS_NOT_ACTIVATED_ON_STARTUP);

            for (int i = 0; i < deltas.length; i++) {
                IExtension extension = deltas[i].getExtension();
                if (deltas[i].getKind() == IExtensionDelta.REMOVED) {
                    continue;
                }

                // if the plugin is not in the set of disabled plugins,
 // then
 // execute the code to start it
 if (disabledPlugins.indexOf(extension.getNamespace()) == -1) {
                    SafeRunner.run(new EarlyStartupRunnable(extension));
                }
            }

        }
    };

    private String factoryID;

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.IWorkbench#getThemeManager()
     */
    public IThemeManager getThemeManager() {
        return WorkbenchThemeManager.getInstance();
    }

    /**
     * Returns <code>true</code> if the workbench is running,
     * <code>false</code> if it has been terminated.
     *
     * @return <code>true</code> if the workbench is running,
     * <code>false</code> if it has been terminated.
     */
    public boolean isRunning() {
        return runEventLoop;
    }

    /**
     * Return the presentation ID specified by the preference or the default ID
     * if undefined.
     *
     * @return the presentation ID
     * @see IWorkbenchPreferenceConstants#PRESENTATION_FACTORY_ID
     */
    public String getPresentationId() {
        if (factoryID != null) {
            return factoryID;
        }

        factoryID = PrefUtil.getAPIPreferenceStore().getString(
                IWorkbenchPreferenceConstants.PRESENTATION_FACTORY_ID);

        // Workaround for bug 58975 - New preference mechanism does not properly
 // initialize defaults
 // Ensure that the UI plugin has started too.
 if (factoryID == null || factoryID.equals("")) { //$NON-NLS-1$
 factoryID = IWorkbenchConstants.DEFAULT_PRESENTATION_ID;
        }
        return factoryID;
    }

    /**
     * <p>
     * Indicates the start of a large update within the workbench. This is used
     * to disable CPU-intensive, change-sensitive services that were temporarily
     * disabled in the midst of large changes. This method should always be
     * called in tandem with <code>largeUpdateEnd</code>, and the event loop
     * should not be allowed to spin before that method is called.
     * </p>
     * <p>
     * Important: always use with <code>largeUpdateEnd</code>!
     * </p>
     */
    public final void largeUpdateStart() {
        if (largeUpdates++ == 0) {
            // TODO Consider whether these lines still need to be here.
 // workbenchCommandSupport.setProcessing(false);
 // workbenchContextSupport.setProcessing(false);

            final IWorkbenchWindow[] windows = getWorkbenchWindows();
            for (int i = 0; i < windows.length; i++) {
                IWorkbenchWindow window = windows[i];
                if (window instanceof WorkbenchWindow) {
                    ((WorkbenchWindow) window).largeUpdateStart();
                }
            }
        }
    }

    /**
     * <p>
     * Indicates the end of a large update within the workbench. This is used to
     * re-enable services that were temporarily disabled in the midst of large
     * changes. This method should always be called in tandem with
     * <code>largeUpdateStart</code>, and the event loop should not be
     * allowed to spin before this method is called.
     * </p>
     * <p>
     * Important: always protect this call by using <code>finally</code>!
     * </p>
     */
    public final void largeUpdateEnd() {
        if (--largeUpdates == 0) {
            // TODO Consider whether these lines still need to be here.
 // workbenchCommandSupport.setProcessing(true);
 // workbenchContextSupport.setProcessing(true);

            // Perform window-specific blocking.
 final IWorkbenchWindow[] windows = getWorkbenchWindows();
            for (int i = 0; i < windows.length; i++) {
                IWorkbenchWindow window = windows[i];
                if (window instanceof WorkbenchWindow) {
                    ((WorkbenchWindow) window).largeUpdateEnd();
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.IWorkbench#getExtensionTracker()
     */
    public IExtensionTracker getExtensionTracker() {
        if (tracker == null) {
            tracker = new UIExtensionTracker(getDisplay());
        }
        return tracker;
    }

    /**
     * Adds the listener that handles startup plugins
     *
     * @since 3.1
     */
    private void addStartupRegistryListener() {
        IExtensionRegistry registry = Platform.getExtensionRegistry();
        registry.addRegistryChangeListener(startupRegistryListener);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.IWorkbench#getHelpSystem()
     */
    public IWorkbenchHelpSystem getHelpSystem() {
        return WorkbenchHelpSystem.getInstance();
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.IWorkbench#getHelpSystem()
     */
    public IWorkbenchBrowserSupport getBrowserSupport() {
        return WorkbenchBrowserSupport.getInstance();
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.IWorkbench#getViewRegistry()
     */
    public IViewRegistry getViewRegistry() {
        return WorkbenchPlugin.getDefault().getViewRegistry();
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.IWorkbench#getNewWizardRegistry()
     */
    public IWizardRegistry getNewWizardRegistry() {
        return WorkbenchPlugin.getDefault().getNewWizardRegistry();
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.IWorkbench#getImportWizardRegistry()
     */
    public IWizardRegistry getImportWizardRegistry() {
        return WorkbenchPlugin.getDefault().getImportWizardRegistry();
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.IWorkbench#getExportWizardRegistry()
     */
    public IWizardRegistry getExportWizardRegistry() {
        return WorkbenchPlugin.getDefault().getExportWizardRegistry();
    }

    public final Object getAdapter(final Class key) {
        return serviceLocator.getService(key);
    }

    private void doRestoreState(final IMemento memento, final MultiStatus status) {
        IMemento childMem;
        try {
            UIStats.start(UIStats.RESTORE_WORKBENCH, "MRUList"); //$NON-NLS-1$
 IMemento mruMemento = memento
                    .getChild(IWorkbenchConstants.TAG_MRU_LIST);
            if (mruMemento != null) {
                status.add(getEditorHistory().restoreState(mruMemento));
            }
        } finally {
            UIStats.end(UIStats.RESTORE_WORKBENCH, this, "MRUList"); //$NON-NLS-1$
 }

        // Restore advisor state.
 IMemento advisorState = memento
                .getChild(IWorkbenchConstants.TAG_WORKBENCH_ADVISOR);
        if (advisorState != null) {
            status.add(getAdvisor().restoreState(advisorState));
        }

        // Get the child windows.
 IMemento[] children = memento
                .getChildren(IWorkbenchConstants.TAG_WINDOW);

        createdWindows = new WorkbenchWindow[children.length];

        // Read the workbench windows.
 for (int i = 0; i < children.length; i++) {
            childMem = children[i];
            final WorkbenchWindow [] newWindow = new WorkbenchWindow[1];
            
            StartupThreading.runWithoutExceptions(new StartupRunnable() {

                public void runWithException() {
                    newWindow[0] = newWorkbenchWindow();
                    newWindow[0].create();
                }});
            createdWindows[i] = newWindow[0];

            // allow the application to specify an initial perspective to open
 // @issue temporary workaround for ignoring initial perspective
 // String initialPerspectiveId =
 // getAdvisor().getInitialWindowPerspectiveId();
 // if (initialPerspectiveId != null) {
 // IPerspectiveDescriptor desc =
 // getPerspectiveRegistry().findPerspectiveWithId(initialPerspectiveId);
 // result.merge(newWindow.restoreState(childMem, desc));
 // }
 // add the window so that any work done in newWindow.restoreState
 // that relies on Workbench methods has windows to work with
 windowManager.add(newWindow[0]);

            // now that we've added it to the window manager we need to listen
 // for any exception that might hose us before we get a chance to
 // open it. If one occurs, remove the new window from the manager.
 // Assume that the new window is a phantom for now
 boolean restored = false;
            try {
                status.merge(newWindow[0].restoreState(childMem, null));
                try {
                    newWindow[0].fireWindowRestored();
                } catch (WorkbenchException e) {
                    status.add(e.getStatus());
                }
                // everything worked so far, don't close now
 restored = true;
            } finally {
                if (!restored) {
                    // null the window in newWindowHolder so that it won't be
 // opened later on
 createdWindows[i] = null;
                    StartupThreading.runWithoutExceptions(new StartupRunnable() {

                        public void runWithException() throws Throwable {
                            newWindow[0].close();
                        }});
                }
            }
        }
    }

    private void openWindowsAfterRestore() {
        if (createdWindows == null) {
            return;
        }
        // now open the windows (except the ones that were nulled because we
 // closed them above)
 for (int i = 0; i < createdWindows.length; i++) {
            if (createdWindows[i] != null) {
                final WorkbenchWindow myWindow = createdWindows[i];
                StartupThreading.runWithoutExceptions(new StartupRunnable() {

                    public void runWithException() throws Throwable {
                        boolean opened = false;
                        try {
                            myWindow.open();
                            opened = true;
                        } finally {
                            if (!opened) {
                                myWindow.close();
                            }
                        }
                    }});
            }
        }
        createdWindows = null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.services.IServiceLocator#getService(java.lang.Object)
     */
    public final Object getService(final Class key) {
        return serviceLocator.getService(key);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.services.IServiceLocator#hasService(java.lang.Object)
     */
    public final boolean hasService(final Class key) {
        return serviceLocator.hasService(key);
    }

    /**
     * Registers a service with this locator. If there is an existing service
     * matching the same <code>api</code> and it implements
     * {@link IDisposable}, it will be disposed.
     *
     * @param api
     * This is the interface that the service implements. Must not be
     * <code>null</code>.
     * @param service
     * The service to register. This must be some implementation of
     * <code>api</code>. This value must not be <code>null</code>.
     */
    public final void registerService(final Class api, final Object service) {
        serviceLocator.registerService(api, service);
    }

    /**
     * The source provider that tracks which context menus (i.e., menus with
     * target identifiers) are now showing. This value is <code>null</code>
     * until {@link #initializeDefaultServices()} is called.
     */
    private MenuSourceProvider menuSourceProvider;


    /**
     * Adds the ids of a menu that is now showing to the menu source provider.
     * This is used for legacy action-based handlers which need to become active
     * only for the duration of a menu being visible.
     *
     * @param menuIds
     * The identifiers of the menu that is now showing; must not be
     * <code>null</code>.
     */
    public final void addShowingMenus(final Set menuIds,
            final ISelection localSelection, final ISelection localEditorInput) {
        menuSourceProvider.addShowingMenus(menuIds, localSelection,
                localEditorInput);
    }

    /**
     * Removes the ids of a menu that is now hidden from the menu source
     * provider. This is used for legacy action-based handlers which need to
     * become active only for the duration of a menu being visible.
     *
     * @param menuIds
     * The identifiers of the menu that is now hidden; must not be
     * <code>null</code>.
     */
    public final void removeShowingMenus(final Set menuIds,
            final ISelection localSelection, final ISelection localEditorInput) {
        menuSourceProvider.removeShowingMenus(menuIds, localSelection,
                localEditorInput);
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.IWorkbench#saveAll(org.eclipse.jface.window.IShellProvider, org.eclipse.jface.operation.IRunnableContext, org.eclipse.ui.ISaveableFilter, boolean)
     */
    public boolean saveAll(IShellProvider shellProvider,
            IRunnableContext runnableContext, ISaveableFilter filter,
            boolean confirm) {
        SaveablesList saveablesList = (SaveablesList) PlatformUI.getWorkbench()
                .getService(ISaveablesLifecycleListener.class);
        Saveable[] saveables = saveablesList.getOpenModels();
        List toSave = getFilteredSaveables(filter, saveables);
        if (toSave.isEmpty())
            return true;
        
        if (!confirm) {
            return !saveablesList.saveModels(toSave, shellProvider, runnableContext);
        }
        
        // We must negate the result since false is cancel saveAll
 return !saveablesList.promptForSaving(toSave, shellProvider, runnableContext, true, false);
    }

    /*
     * Apply the given filter to the list of saveables
     */
    private List getFilteredSaveables(ISaveableFilter filter, Saveable[] saveables) {
        List toSave = new ArrayList ();
        if (filter == null) {
            for (int i = 0; i < saveables.length; i++) {
                Saveable saveable = saveables[i];
                if (saveable.isDirty())
                    toSave.add(saveable);
            }
        } else {
            SaveablesList saveablesList = (SaveablesList)getService(ISaveablesLifecycleListener.class);
            for (int i = 0; i < saveables.length; i++) {
                Saveable saveable = saveables[i];
                if (saveable.isDirty()) {
                    IWorkbenchPart[] parts = saveablesList.getPartsForSaveable(saveable);
                    if (matchesFilter(filter, saveable, parts))
                        toSave.add(saveable);
                }
            }
        }
        return toSave;
    }
    
    /*
     * Test whether the given filter matches the saveable
     */
    private boolean matchesFilter(ISaveableFilter filter, Saveable saveable,
            IWorkbenchPart[] parts) {
        return filter == null || filter.select(saveable, parts);
    }
}

